From 96eb6ebe2e109641ad28900b3020ca7b7bce43ee Mon Sep 17 00:00:00 2001 From: LiangQuan Date: Mon, 4 Dec 2023 13:19:47 +0800 Subject: [PATCH 01/59] Use literal version instead of scm version. (#2) * Use literal version instead of scm version. * Update version info --------- --- docs/source/conf.py | 5 +++-- pai/__init__.py | 16 +--------------- pai/common/utils.py | 4 ++-- pai/version.py | 15 +++++++++++++++ setup.py | 7 ++++++- 5 files changed, 27 insertions(+), 20 deletions(-) create mode 100644 pai/version.py diff --git a/docs/source/conf.py b/docs/source/conf.py index 915cfd5..ecdcf76 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -17,6 +17,7 @@ doc_root_dir = dirname(dirname(os.path.abspath(__file__))) sys.path.insert(0, dirname(doc_root_dir)) +pkg_root = dirname(doc_root_dir) import pai @@ -27,7 +28,8 @@ author = "Alibaba Cloud" # The full version, including alpha/beta/rc tags -release = pai.__version__ +release = pai.version.VERSION +version = pai.version.VERSION # -- General configuration --------------------------------------------------- @@ -37,7 +39,6 @@ # ones. extensions = [ "sphinx.ext.autodoc", - # "sphinx.ext.viewcode", "sphinx.ext.napoleon", "sphinx_copybutton", "sphinx_markdown_builder", diff --git a/pai/__init__.py b/pai/__init__.py index 71e8bc4..55b7955 100644 --- a/pai/__init__.py +++ b/pai/__init__.py @@ -12,18 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -import sys - -if sys.version_info >= (3, 8): - # noinspection PyCompatibility - from importlib.metadata import PackageNotFoundError, version -else: - from importlib_metadata import PackageNotFoundError, version - -PACKAGE_NAME = "alipai" - -try: - __version__ = version(PACKAGE_NAME) -except PackageNotFoundError: - # package is not installed - __version__ = None +from .version import VERSION as __version__ diff --git a/pai/common/utils.py b/pai/common/utils.py index e5aada1..2e3c654 100644 --- a/pai/common/utils.py +++ b/pai/common/utils.py @@ -24,12 +24,12 @@ from semantic_version import Version -from pai import __version__ from pai.common.consts import ( INSTANCE_TYPE_LOCAL, INSTANCE_TYPE_LOCAL_GPU, FileSystemInputScheme, ) +from pai.version import VERSION DEFAULT_PLAIN_TEXT_ALLOW_CHARACTERS = string.ascii_letters + string.digits + "_" @@ -106,7 +106,7 @@ def to_plain_text( def http_user_agent(user_agent: Optional[Union[Dict, str]] = None) -> str: """Generate HTTP User-Agent that represents current client.""" - ua = f"pai-python-sdk/{__version__}; python/{sys.version.split()[0]}" + ua = f"pai-python-sdk/{VERSION}; python/{sys.version.split()[0]}" if isinstance(user_agent, dict): ua += "; " + "; ".join(f"{k}/{v}" for k, v in user_agent.items()) elif isinstance(user_agent, str): diff --git a/pai/version.py b/pai/version.py new file mode 100644 index 0000000..7e627c0 --- /dev/null +++ b/pai/version.py @@ -0,0 +1,15 @@ +# Copyright 2023 Alibaba, Inc. or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +VERSION = "0.4.5.dev0" diff --git a/setup.py b/setup.py index 520c7df..43d405d 100644 --- a/setup.py +++ b/setup.py @@ -8,6 +8,11 @@ REQUIREMENTS_FILE = "requirements/requirements.txt" +version_data = {} +with open(os.path.join(pkg_root, "pai/version.py")) as fp: + exec(fp.read(), version_data) +version = version_data["VERSION"] + def read_requirements(): with open(os.path.join(pkg_root, REQUIREMENTS_FILE), "r") as f: @@ -22,7 +27,7 @@ def read_requirements(): setup( name="alipai", python_requires=">=3.6", - use_scm_version=True, + version=version, setup_requires=["setuptools_scm"], description="Alibaba Cloud PAI Python SDK", long_description=long_description, From 99df4ed7d1609630109ceb1e5f36b24116268554 Mon Sep 17 00:00:00 2001 From: LiangQuan Date: Mon, 4 Dec 2023 13:43:42 +0800 Subject: [PATCH 02/59] Host documentation in ReadTheDocs. (#3) --- README.md | 4 ++-- README_CN.md | 4 ++-- tools/publish_doc.sh | 51 -------------------------------------------- 3 files changed, 4 insertions(+), 55 deletions(-) delete mode 100755 tools/publish_doc.sh diff --git a/README.md b/README.md index 77e2d45..346aaed 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ python -m pip install alipai ## Documentation 📖 -Find detailed documentation, including API references and user guides, in the [docs]{.title-ref} directory or visit [PAI Python SDK Documentation](https://pai-sdk.oss-cn-shanghai.aliyuncs.com/pai/doc/latest/index.html). +Find detailed documentation, including API references and user guides, in the [docs]{.title-ref} directory or visit [PAI Python SDK Documentation](https://alipai.readthedocs.io/). ## Basic Usage 🛠 @@ -67,7 +67,7 @@ p.predict( ) ``` -For more details, please refer to the [PAI Python SDK Documentation](https://pai-sdk.oss-cn-shanghai.aliyuncs.com/pai/doc/latest/index.html). +For more details, please refer to the [PAI Python SDK Documentation](https://alipai.readthedocs.io/). ## Contributing 🤝 diff --git a/README_CN.md b/README_CN.md index 3ec96a6..05b3932 100644 --- a/README_CN.md +++ b/README_CN.md @@ -14,7 +14,7 @@ python -m pip install alipai ## 文档 📖 -请通过访问 [PAI Python SDK文档](https://pai-sdk.oss-cn-shanghai.aliyuncs.com/pai/doc/latest/index.html) 或是查看 [docs](./docs) 目录下的文件获取SDK的详细文档,包括用户指南和API文档。 +请通过访问 [PAI Python SDK文档](https://alipai.readthedocs.io/) 或是查看 [docs](./docs) 目录下的文件获取SDK的详细文档,包括用户指南和API文档。 ## 使用示例 🛠 @@ -63,7 +63,7 @@ p.predict( ) ``` -更多功能介绍,请参阅 [PAI Python SDK文档](https://pai-sdk.oss-cn-shanghai.aliyuncs.com/pai/doc/latest/index.html)。 +更多功能介绍,请参阅 [PAI Python SDK文档](https://alipai.readthedocs.io/) 。 ## 贡献代码 🤝 diff --git a/tools/publish_doc.sh b/tools/publish_doc.sh deleted file mode 100755 index b46f620..0000000 --- a/tools/publish_doc.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash - -#To use the script, you must have ossutil in your system PATH and config the client tool to visit the "pai-sdk" OSS bucket. -# -#usage: -# -# publish_doc preview # publish preview documents -# publish_doc production # publish production documents -# - -set -e - -function cd_docs_dir() { - docs_dir="$(dirname "$0")/../docs" - cd "$docs_dir" || (echo "cd the workdir $(docs_dir) fail" && exit) -} - -function build_doc() { - nox -s doc -} - -function publish_preview_doc() { - build_doc - public_doc preview/doc/html - echo "please visit link https://pai-sdk.oss-cn-shanghai.aliyuncs.com/pai/preview/doc/html/index.html to view the documents." -} - -function public_production_doc() { - build_doc - public_doc doc/html - echo "please visit link https://pai-sdk.oss-cn-shanghai.aliyuncs.com/pai/doc/html/index.html to view the documents." -} -# -function public_doc() { - if [ -z "$1" ]; then - echo "target_path not exists" && exit 1 - fi - echo "target_path is $1" - - ossutilmac64 cp build/html oss://pai-sdk/pai/"$1" --recursive -f -} - -release_type=${1:-preview} - -if [ $release_type == "preview" ]; then - publish_preview_doc -elif [ $release_type == "production" ]; then - public_production_doc -else - echo "unknown release_type: $release_type" && exit 1 -fi From 124429a4b20e22e177b6541521d14f9570f72e83 Mon Sep 17 00:00:00 2001 From: LiangQuan Date: Wed, 6 Dec 2023 18:58:15 +0800 Subject: [PATCH 03/59] Update README.md (#4) --- README.md | 4 +++- README_CN.md | 4 +++- assets/dingtalk-group.png | Bin 0 -> 319657 bytes assets/logo.svg | 1 + 4 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 assets/dingtalk-group.png create mode 100644 assets/logo.svg diff --git a/README.md b/README.md index 346aaed..aaf9385 100644 --- a/README.md +++ b/README.md @@ -79,4 +79,6 @@ PAI Python SDK is developed by Alibaba Cloud and licensed under the Apache Licen ## Contact 📬 -For support or inquiries, please open an issue on the GitHub repository. +For support or inquiries, please open an issue on the GitHub repository or contact us in the DingTalk group: + +DingTalkGroup diff --git a/README_CN.md b/README_CN.md index 05b3932..942bbcb 100644 --- a/README_CN.md +++ b/README_CN.md @@ -75,4 +75,6 @@ PAI Python SDK是由阿里云开发,并根据Apache许可证(版本2.0)授 ## 联系方式 📬 -如需支持或咨询,请在GitHub仓库中提Issue。 +如需支持或咨询,请在GitHub仓库中提交issue,或通过钉钉群联系我们: + +DingTalkGroup diff --git a/assets/dingtalk-group.png b/assets/dingtalk-group.png new file mode 100644 index 0000000000000000000000000000000000000000..012aed3345df7a69fd8699e4dbfc5ca38a23f149 GIT binary patch literal 319657 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�v^j%Q$sY)V0&O?v+~dzr|I6?9 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�+++`*Fyp4pvdQWAmYCR9vl!ywgDL70`iF54ES2| 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�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

w7SlGoKtAVESi?;a$2IKI&4 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%0A-)>+qIyU22M~m%>hrF}Zo!#Y2N$T~r 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 Y{reMhOD?R-Fxf7egwRe=F#o*!UwdFZ(*OVf diff --git a/docs/source/tutorial/checkpoint/checkpoint.ipynb b/docs/source/tutorial/checkpoint/checkpoint.ipynb deleted file mode 100644 index 55cfbff..0000000 --- a/docs/source/tutorial/checkpoint/checkpoint.ipynb +++ /dev/null @@ -1,1285 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# 在训练作业中使用checkpoint\n", - "\n", - "在机器学习模型训练过程中,往往需要较长的时间完成训练数据的迭代,实现模型的收敛,然而训练过程可能会因为各种原因中断,例如机器故障、网络问题、或是代码原因等。为了避免中断后需要重头开始训练,开发者通常会在训练过程中,定期将模型的状态保存为`checkpoint`文件,以便在训练中断后,能够从保存的`checkpoint`文件获取模型参数,优化器状态,训练步数等训练状态,恢复训练。\n", - "\n", - "本文档介绍如何在PAI的训练作业中使用checkpoint。" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "## 准备工作\n", - "\n", - "我们需要首先安装PAI Python SDK以运行本示例。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!python -m pip install --upgrade alipai" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "SDK 需要配置访问阿里云服务需要的 AccessKey,以及当前使用的工作空间和OSS Bucket。在 PAI Python SDK 安装之后,通过在 **命令行终端** 中执行以下命令,按照引导配置密钥,工作空间等信息。\n", - "\n", - "\n", - "```shell\n", - "\n", - "# 以下命令,请在 命令行终端 中执行.\n", - "\n", - "python -m pai.toolkit.config\n", - "\n", - "```\n", - "\n", - "\n", - "我们可以通过以下代码验证当前的配置。" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 使用checkpoint保存和恢复训练作业\n", - "\n", - "当使用SDK提供的`pai.estimator.Estimator` 提交训练作业时,训练作业默认会挂载用户的OSS Bucket路径到训练作业的`/ml/output/checkpoints`目录。训练代码可以将checkpoint文件写出到对应的路径,从而保存到OSS中。提交训练作业之后,可以通过 `estimator.checkpoints_data()` 方法可以获取`checkpoints`保存的OSS路径。\n", - "\n", - "当需要使用已有的`checkpoint`时,用户可以通过 `checkpoints_path` 参数指定一个OSS Bucket路径,PAI会将该路径挂载到训练作业的`/ml/output/checkpoints`目录,训练作业可以通过读取对应数据路径下的checkpoint文件来恢复训练。\n", - "\n", - "\n", - "\n", - "```python\n", - "\n", - "from pai.estimator import Estimator\n", - "\n", - "\n", - "# 1. 使用默认的checkpoints路径保存模型的checkpoints\n", - "est = Estimator(\n", - "\timage_uri=\"\",\n", - "\tcommand=\"python train.py\",\n", - ")\n", - "\n", - "# 训练作业默认会挂载一个OSS Bucket路径到 /ml/output/checkpoints\n", - "# 用户训练代码可以通过写文件到 /ml/output/checkpoints 保存checkpoint\n", - "est.fit()\n", - "\n", - "# 查看训练作业的checkpoints路径\n", - "print(est.checkpoints_data())\n", - "\n", - "# 2. 使用其他训练作业产出的checkpoints恢复训练\n", - "est_load = Estimator(\n", - "\timage_uri=\"\",\n", - "\tcommand=\"python train.py\",\n", - "\t# 指定使用上一个训练作业输出的checkpoints.\n", - "\tcheckpoints_path=est.checkpoints_data(),\n", - ")\n", - "\n", - "# 训练代码从 /ml/output/checkpoints 中加载checkpoint\n", - "est_load.fit()\n", - "\n", - "```\n", - "\n", - "\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 在PyTorch中使用checkpoint\n", - "\n", - "在PyTorch中,通常使用`torch.save`方法将模型的参数、优化器的状态、训练进度等信息,以字典的形式作为`checkpoint`进行保存。保存的`checkpoint`文件可以通过 `torch.load` 进行加载。PyTorch提供了如何在训练中保存和加载checkpoint的教程:[Save And Loading A General Checkpoint In PyTorch](https://pytorch.org/tutorials/recipes/recipes/saving_and_loading_a_general_checkpoint.html)。\n", - "\n", - "我们将基于PyTorch的示例教程,演示如何在PAI的训练作业中使用checkpoint。\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "训练作业使用的代码如下:\n", - "\n", - "1. 在训练开始之前,通过 `/ml/output/checkpoints/` 路径加载checkpoint获取初始化模型参数,优化器,以及训练进度。\n", - "\n", - "2. 基于checkpoint的状态信息训练模型,在训练过程中,定期保存checkpoint到 `/ml/output/checkpoints/` 路径。\n", - " " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!mkdir -p train_src" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%%writefile train_src/train.py\n", - "# Additional information\n", - "import os\n", - "import torch\n", - "import torch.nn as nn\n", - "import torch.optim as optim\n", - "from torch.utils.data import Dataset, DataLoader\n", - "import torch.nn.functional as F\n", - "\n", - "\n", - "EPOCH = 5\n", - "CHECKPOINT_NAME = \"checkpoint.pt\"\n", - "LOSS = 0.4\n", - "\n", - "# Define a custom mock dataset\n", - "class RandomDataset(Dataset):\n", - " def __init__(self, num_samples=1000):\n", - " self.num_samples = num_samples\n", - "\n", - " def __len__(self):\n", - " return self.num_samples\n", - "\n", - " def __getitem__(self, idx):\n", - " x = torch.randn(10) # Generating random input tensor\n", - " y = torch.randint(0, 2, (1,)).item() # Generating random target label (0 or 1)\n", - " return x, y\n", - "\n", - "\n", - "# Define your model\n", - "class MyModel(nn.Module):\n", - " def __init__(self):\n", - " super(MyModel, self).__init__()\n", - " self.fc = nn.Linear(10, 2)\n", - " \n", - " def forward(self, x):\n", - " return self.fc(x)\n", - "\n", - "\n", - "net = MyModel()\n", - "criterion = nn.CrossEntropyLoss()\n", - "optimizer = optim.SGD(net.parameters(), lr=0.001)\n", - "start_epoch = 0\n", - "\n", - "def load_checkpoint():\n", - " \"\"\"Load checkpoint if exists.\"\"\"\n", - " global net, optimizer, start_epoch, LOSS\n", - " checkpoint_dir = os.environ.get(\"PAI_OUTPUT_CHECKPOINTS\")\n", - " if not checkpoint_dir:\n", - " return\n", - " checkpoint_path = os.path.join(checkpoint_dir, CHECKPOINT_NAME)\n", - " if not os.path.exists(checkpoint_path):\n", - " return\n", - " data = torch.load(checkpoint_path)\n", - "\n", - " net.load_state_dict(data[\"model_state_dict\"])\n", - " optimizer.load_state_dict(data[\"optimizer_state_dict\"])\n", - " start_epoch = data[\"epoch\"]\n", - "\n", - "\n", - "def save_checkpoint(epoch):\n", - " global net, optimizer, start_epoch, LOSS\n", - " checkpoint_dir = os.environ.get(\"PAI_OUTPUT_CHECKPOINTS\")\n", - " if not checkpoint_dir:\n", - " return\n", - " checkpoint_path = os.path.join(checkpoint_dir, CHECKPOINT_NAME)\n", - " torch.save({\n", - " 'epoch': epoch + 1,\n", - " 'model_state_dict': net.state_dict(),\n", - " 'optimizer_state_dict': optimizer.state_dict(),\n", - " }, checkpoint_path)\n", - "\n", - "\n", - "def parse_args():\n", - " import argparse\n", - " parser = argparse.ArgumentParser()\n", - " parser.add_argument(\"--epochs\", type=int, default=10)\n", - " args = parser.parse_args()\n", - " return args\n", - "\n", - "\n", - "def train():\n", - " args = parse_args()\n", - " load_checkpoint()\n", - " batch_size = 4\n", - " dataloader = DataLoader(RandomDataset(), batch_size=batch_size, shuffle=True)\n", - " num_epochs = args.epochs\n", - " print(num_epochs)\n", - " for epoch in range(start_epoch, num_epochs):\n", - " net.train()\n", - " for i, (inputs, targets) in enumerate(dataloader):\n", - " # Forward pass\n", - " outputs = net(inputs)\n", - " loss = criterion(outputs, targets)\n", - " \n", - " # Backward pass and optimization\n", - " optimizer.zero_grad()\n", - " loss.backward()\n", - " optimizer.step()\n", - " \n", - " # Print training progress\n", - " if (i+1) % 10 == 0:\n", - " print(f'Epoch [{epoch+1}/{num_epochs}], Step [{i+1}/{len(dataloader)}], Loss: {loss.item()}')\n", - " \n", - " # Save checkpoint\n", - " save_checkpoint(epoch=epoch)\n", - " # save the model\n", - " torch.save(net.state_dict(), os.path.join(os.environ.get(\"PAI_OUTPUT_MODEL\", \".\"), \"model.pt\"))\n", - " \n", - "\n", - "\n", - "if __name__ == \"__main__\":\n", - " train()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "我们将以上的代码提交到PAI执行,训练作业最终提供挂载的OSS路径保存模型。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [ - "keep_output" - ] - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/liangquan/code/pypai/pai/common/oss_utils.py:13: TqdmExperimentalWarning: Using `tqdm.autonotebook.tqdm` in notebook mode. Use `tqdm.tqdm` instead to force console mode (e.g. in jupyter console)\n", - " from tqdm.autonotebook import tqdm\n" - ] - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "d8266991a0d042c6a54531f252ecc727", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Uploading file: /var/folders/hc/5w4bg25j1ns2mm0yb06zzzbh0000gp/T/tmpt3_0rsuf/source.tar.gz: 0%| | 0…" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "View the job detail by accessing the console URI: https://pai.console.aliyun.com/?regionId=cn-hangzhou&workspaceId=58670#/training/jobs/train1u1it512gqg\n", - "TrainingJob launch starting\n", - "MAX_PARALLELISM=0\n", - "C_INCLUDE_PATH=/home/pai/include\n", - "KUBERNETES_PORT=tcp://10.192.0.1:443\n", - "KUBERNETES_SERVICE_PORT=443\n", - "LANGUAGE=en_US.UTF-8\n", - "PIP_TRUSTED_HOST=mirrors.cloud.aliyuncs.com\n", - "MASTER_ADDR=train1u1it512gqg-master-0\n", - "HOSTNAME=train1u1it512gqg-master-0\n", - "LD_LIBRARY_PATH=:/lib/x86_64-linux-gnu:/home/pai/lib:/home/pai/jre/lib/amd64/server\n", - "MASTER_PORT=23456\n", - "HOME=/root\n", - "PAI_USER_ARGS=\n", - "PYTHONUNBUFFERED=0\n", - "PAI_OUTPUT_CHECKPOINTS=/ml/output/checkpoints/\n", - "PAI_CONFIG_DIR=/ml/input/config/\n", - "WORLD_SIZE=1\n", - "REGION_ID=cn-hangzhou\n", - "CPLUS_INCLUDE_PATH=/home/pai/include\n", - "RANK=0\n", - "OPAL_PREFIX=/home/pai/\n", - "PAI_TRAINING_JOB_ID=train1u1it512gqg\n", - "TERM=xterm-color\n", - "KUBERNETES_PORT_443_TCP_ADDR=10.192.0.1\n", - "PAI_OUTPUT_MODEL=/ml/output/model/\n", - "ELASTIC_TRAINING_ENABLED=false\n", - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/home/pai/bin:/home/pai/hadoop/bin\n", - "PIP_INDEX_URL=https://mirrors.cloud.aliyuncs.com/pypi/simple\n", - "KUBERNETES_PORT_443_TCP_PORT=443\n", - "KUBERNETES_PORT_443_TCP_PROTO=tcp\n", - "LANG=en_US.UTF-8\n", - "aliyun_logs_containerType_tags=containerType=Algorithm\n", - "PAI_TRAINING_USE_ECI=true\n", - "KUBERNETES_SERVICE_PORT_HTTPS=443\n", - "KUBERNETES_PORT_443_TCP=tcp://10.192.0.1:443\n", - "ELASTIC_INFERENCE_ENABLED=false\n", - "LC_ALL=en_US.UTF-8\n", - "JAVA_HOME=/home/pai\n", - "KUBERNETES_SERVICE_HOST=10.192.0.1\n", - "PWD=/\n", - "PAI_HPS={}\n", - "TZ=UTC\n", - "HADOOP_HOME=/home/pai/hadoop\n", - "PAI_OUTPUT_LOGS=/ml/output/logs/\n", - "aliyun_logs_trainingJobId_tags=trainingJobId=train1u1it512gqg\n", - "PAI_ODPS_CREDENTIAL=/ml/input/credential/odps.json\n", - "PAI_WORKING_DIR=/ml/usercode/\n", - "Change to Working Directory, /ml/usercode/\n", - "User program launching\n", - "-----------------------------------------------------------------\n", - "10\n", - "Epoch [1/10], Step [10/250], Loss: 0.3664854168891907\n", - "Epoch [1/10], Step [20/250], Loss: 0.5867650508880615\n", - "Epoch [1/10], Step [30/250], Loss: 0.8810225129127502\n", - "Epoch [1/10], Step [40/250], Loss: 1.3596220016479492\n", - "Epoch [1/10], Step [50/250], Loss: 1.0757191181182861\n", - "Epoch [1/10], Step [60/250], Loss: 0.5261836051940918\n", - "Epoch [1/10], Step [70/250], Loss: 1.0891999006271362\n", - "Epoch [1/10], Step [80/250], Loss: 1.2425217628479004\n", - "Epoch [1/10], Step [90/250], Loss: 0.7928518652915955\n", - "Epoch [1/10], Step [100/250], Loss: 0.500701367855072\n", - "Epoch [1/10], Step [110/250], Loss: 1.1105762720108032\n", - "Epoch [1/10], Step [120/250], Loss: 0.7642831802368164\n", - "Epoch [1/10], Step [130/250], Loss: 0.9435116052627563\n", - "Epoch [1/10], Step [140/250], Loss: 0.4632255434989929\n", - "Epoch [1/10], Step [150/250], Loss: 0.8282555937767029\n", - "Epoch [1/10], Step [160/250], Loss: 0.5644117593765259\n", - "Epoch [1/10], Step [170/250], Loss: 0.8821360468864441\n", - "Epoch [1/10], Step [180/250], Loss: 0.6495410799980164\n", - "Epoch [1/10], Step [190/250], Loss: 0.6814499497413635\n", - "Epoch [1/10], Step [200/250], Loss: 1.1818656921386719\n", - "Epoch [1/10], Step [210/250], Loss: 0.4218548536300659\n", - "Epoch [1/10], Step [220/250], Loss: 0.5892952680587769\n", - "Epoch [1/10], Step [230/250], Loss: 0.8104468584060669\n", - "Epoch [1/10], Step [240/250], Loss: 0.3310832977294922\n", - "Epoch [1/10], Step [250/250], Loss: 1.0296210050582886\n", - "Epoch [2/10], Step [10/250], Loss: 0.747037947177887\n", - "Epoch [2/10], Step [20/250], Loss: 1.0555682182312012\n", - "Epoch [2/10], Step [30/250], Loss: 0.5005624294281006\n", - "Epoch [2/10], Step [40/250], Loss: 0.6007864475250244\n", - "Epoch [2/10], Step [50/250], Loss: 0.8172819018363953\n", - "Epoch [2/10], Step [60/250], Loss: 0.7322960495948792\n", - "Epoch [2/10], Step [70/250], Loss: 0.6178841590881348\n", - "Epoch [2/10], Step [80/250], Loss: 0.9776118993759155\n", - "Epoch [2/10], Step [90/250], Loss: 0.8088865876197815\n", - "Epoch [2/10], Step [100/250], Loss: 0.7169486284255981\n", - "Epoch [2/10], Step [110/250], Loss: 0.8003190159797668\n", - "Epoch [2/10], Step [120/250], Loss: 0.9178279638290405\n", - "Epoch [2/10], Step [130/250], Loss: 0.5217956900596619\n", - "Epoch [2/10], Step [140/250], Loss: 1.2751939296722412\n", - "Epoch [2/10], Step [150/250], Loss: 1.1024904251098633\n", - "Epoch [2/10], Step [160/250], Loss: 0.6336060762405396\n", - "Epoch [2/10], Step [170/250], Loss: 0.799022376537323\n", - "Epoch [2/10], Step [180/250], Loss: 0.7938567996025085\n", - "Epoch [2/10], Step [190/250], Loss: 1.060591220855713\n", - "Epoch [2/10], Step [200/250], Loss: 0.9365970492362976\n", - "Epoch [2/10], Step [210/250], Loss: 0.6945515871047974\n", - "Epoch [2/10], Step [220/250], Loss: 0.4772261381149292\n", - "Epoch [2/10], Step [230/250], Loss: 1.0332412719726562\n", - "Epoch [2/10], Step [240/250], Loss: 0.7284632325172424\n", - "Epoch [2/10], Step [250/250], Loss: 0.4485410451889038\n", - "Epoch [3/10], Step [10/250], Loss: 0.7845520377159119\n", - "Epoch [3/10], Step [20/250], Loss: 0.5619648694992065\n", - "Epoch [3/10], Step [30/250], Loss: 0.725273609161377\n", - "Epoch [3/10], Step [40/250], Loss: 0.7783026695251465\n", - "Epoch [3/10], Step [50/250], Loss: 0.5168777704238892\n", - "Epoch [3/10], Step [60/250], Loss: 0.67060387134552\n", - "Epoch [3/10], Step [70/250], Loss: 0.9300781488418579\n", - "Epoch [3/10], Step [80/250], Loss: 0.6534505486488342\n", - "Epoch [3/10], Step [90/250], Loss: 0.557340681552887\n", - "Epoch [3/10], Step [100/250], Loss: 0.667724609375\n", - "Epoch [3/10], Step [110/250], Loss: 0.5125826001167297\n", - "Epoch [3/10], Step [120/250], Loss: 0.4494149088859558\n", - "Epoch [3/10], Step [130/250], Loss: 0.6902559995651245\n", - "Epoch [3/10], Step [140/250], Loss: 0.5450549125671387\n", - "Epoch [3/10], Step [150/250], Loss: 1.0632681846618652\n", - "Epoch [3/10], Step [160/250], Loss: 0.7964761257171631\n", - "Epoch [3/10], Step [170/250], Loss: 0.5218536257743835\n", - "Epoch [3/10], Step [180/250], Loss: 0.6972622275352478\n", - "Epoch [3/10], Step [190/250], Loss: 0.7963941097259521\n", - "Epoch [3/10], Step [200/250], Loss: 0.5798731446266174\n", - "Epoch [3/10], Step [210/250], Loss: 0.7930802702903748\n", - "Epoch [3/10], Step [220/250], Loss: 0.7618649005889893\n", - "Epoch [3/10], Step [230/250], Loss: 0.9831617474555969\n", - "Epoch [3/10], Step [240/250], Loss: 0.7935497164726257\n", - "Epoch [3/10], Step [250/250], Loss: 0.9747794270515442\n", - "Epoch [4/10], Step [10/250], Loss: 0.6432996392250061\n", - "Epoch [4/10], Step [20/250], Loss: 0.6515889167785645\n", - "Epoch [4/10], Step [30/250], Loss: 0.8191264867782593\n", - "Epoch [4/10], Step [40/250], Loss: 0.5717310905456543\n", - "Epoch [4/10], Step [50/250], Loss: 1.0365064144134521\n", - "Epoch [4/10], Step [60/250], Loss: 0.7181562185287476\n", - "Epoch [4/10], Step [70/250], Loss: 0.6014276146888733\n", - "Epoch [4/10], Step [80/250], Loss: 0.8743482232093811\n", - "Epoch [4/10], Step [90/250], Loss: 0.5963127613067627\n", - "Epoch [4/10], Step [100/250], Loss: 0.7012943029403687\n", - "Epoch [4/10], Step [110/250], Loss: 0.6271654367446899\n", - "Epoch [4/10], Step [120/250], Loss: 0.646144449710846\n", - "Epoch [4/10], Step [130/250], Loss: 0.5112266540527344\n", - "Epoch [4/10], Step [140/250], Loss: 0.8657329678535461\n", - "Epoch [4/10], Step [150/250], Loss: 0.677897572517395\n", - "Epoch [4/10], Step [160/250], Loss: 0.798669695854187\n", - "Epoch [4/10], Step [170/250], Loss: 0.805213451385498\n", - "Epoch [4/10], Step [180/250], Loss: 0.7744658589363098\n", - "Epoch [4/10], Step [190/250], Loss: 0.4748728275299072\n", - "Epoch [4/10], Step [200/250], Loss: 0.6623726487159729\n", - "Epoch [4/10], Step [210/250], Loss: 0.6851851940155029\n", - "Epoch [4/10], Step [220/250], Loss: 0.5917701721191406\n", - "Epoch [4/10], Step [230/250], Loss: 0.586968719959259\n", - "Epoch [4/10], Step [240/250], Loss: 0.758073091506958\n", - "Epoch [4/10], Step [250/250], Loss: 0.7908360958099365\n", - "Epoch [5/10], Step [10/250], Loss: 0.747495174407959\n", - "Epoch [5/10], Step [20/250], Loss: 0.7880417108535767\n", - "Epoch [5/10], Step [30/250], Loss: 1.4239259958267212\n", - "Epoch [5/10], Step [40/250], Loss: 0.709957480430603\n", - "Epoch [5/10], Step [50/250], Loss: 0.45279955863952637\n", - "Epoch [5/10], Step [60/250], Loss: 0.6855078935623169\n", - "Epoch [5/10], Step [70/250], Loss: 0.7050631046295166\n", - "Epoch [5/10], Step [80/250], Loss: 0.8256967067718506\n", - "Epoch [5/10], Step [90/250], Loss: 0.9627029895782471\n", - "Epoch [5/10], Step [100/250], Loss: 0.7069070339202881\n", - "Epoch [5/10], Step [110/250], Loss: 0.6772119998931885\n", - "Epoch [5/10], Step [120/250], Loss: 0.5547316670417786\n", - "Epoch [5/10], Step [130/250], Loss: 0.4749568998813629\n", - "Epoch [5/10], Step [140/250], Loss: 0.5910231471061707\n", - "Epoch [5/10], Step [150/250], Loss: 0.5789163112640381\n", - "Epoch [5/10], Step [160/250], Loss: 0.994613766670227\n", - "Epoch [5/10], Step [170/250], Loss: 0.7664419412612915\n", - "Epoch [5/10], Step [180/250], Loss: 0.7812412977218628\n", - "Epoch [5/10], Step [190/250], Loss: 0.932634174823761\n", - "Epoch [5/10], Step [200/250], Loss: 0.4732060134410858\n", - "Epoch [5/10], Step [210/250], Loss: 0.6712639927864075\n", - "Epoch [5/10], Step [220/250], Loss: 0.7019771337509155\n", - "Epoch [5/10], Step [230/250], Loss: 0.668921709060669\n", - "Epoch [5/10], Step [240/250], Loss: 0.5486156344413757\n", - "Epoch [5/10], Step [250/250], Loss: 0.8131189346313477\n", - "Epoch [6/10], Step [10/250], Loss: 0.5800281167030334\n", - "Epoch [6/10], Step [20/250], Loss: 0.9032570719718933\n", - "Epoch [6/10], Step [30/250], Loss: 0.6829659938812256\n", - "Epoch [6/10], Step [40/250], Loss: 0.577970027923584\n", - "Epoch [6/10], Step [50/250], Loss: 0.9745671153068542\n", - "Epoch [6/10], Step [60/250], Loss: 0.6292040348052979\n", - "Epoch [6/10], Step [70/250], Loss: 0.9189562201499939\n", - "Epoch [6/10], Step [80/250], Loss: 1.0687212944030762\n", - "Epoch [6/10], Step [90/250], Loss: 0.6210573315620422\n", - "Epoch [6/10], Step [100/250], Loss: 0.7758654356002808\n", - "Epoch [6/10], Step [110/250], Loss: 1.055539846420288\n", - "Epoch [6/10], Step [120/250], Loss: 0.7991855144500732\n", - "Epoch [6/10], Step [130/250], Loss: 0.8390480279922485\n", - "Epoch [6/10], Step [140/250], Loss: 0.5641282200813293\n", - "Epoch [6/10], Step [150/250], Loss: 0.5416208505630493\n", - "Epoch [6/10], Step [160/250], Loss: 0.8556939363479614\n", - "Epoch [6/10], Step [170/250], Loss: 0.8848042488098145\n", - "Epoch [6/10], Step [180/250], Loss: 0.6585526466369629\n", - "Epoch [6/10], Step [190/250], Loss: 0.5264347791671753\n", - "Epoch [6/10], Step [200/250], Loss: 0.7451325058937073\n", - "Epoch [6/10], Step [210/250], Loss: 0.8498039841651917\n", - "Epoch [6/10], Step [220/250], Loss: 0.9514821767807007\n", - "Epoch [6/10], Step [230/250], Loss: 0.5831080675125122\n", - "Epoch [6/10], Step [240/250], Loss: 0.7323013544082642\n", - "Epoch [6/10], Step [250/250], Loss: 0.799047589302063\n", - "Epoch [7/10], Step [10/250], Loss: 0.7431624531745911\n", - "Epoch [7/10], Step [20/250], Loss: 0.7462856769561768\n", - "Epoch [7/10], Step [30/250], Loss: 0.7637103796005249\n", - "Epoch [7/10], Step [40/250], Loss: 0.7512863874435425\n", - "Epoch [7/10], Step [50/250], Loss: 0.8934370279312134\n", - "Epoch [7/10], Step [60/250], Loss: 0.6657339334487915\n", - "Epoch [7/10], Step [70/250], Loss: 0.7996265292167664\n", - "Epoch [7/10], Step [80/250], Loss: 0.7883811593055725\n", - "Epoch [7/10], Step [90/250], Loss: 0.7327611446380615\n", - "Epoch [7/10], Step [100/250], Loss: 0.7103905081748962\n", - "Epoch [7/10], Step [110/250], Loss: 0.8145009875297546\n", - "Epoch [7/10], Step [120/250], Loss: 0.6999544501304626\n", - "Epoch [7/10], Step [130/250], Loss: 0.6132965087890625\n", - "Epoch [7/10], Step [140/250], Loss: 0.8219666481018066\n", - "Epoch [7/10], Step [150/250], Loss: 0.573877215385437\n", - "Epoch [7/10], Step [160/250], Loss: 0.864593505859375\n", - "Epoch [7/10], Step [170/250], Loss: 0.7187140583992004\n", - "Epoch [7/10], Step [180/250], Loss: 0.601334810256958\n", - "Epoch [7/10], Step [190/250], Loss: 0.6193158626556396\n", - "Epoch [7/10], Step [200/250], Loss: 0.7600311040878296\n", - "Epoch [7/10], Step [210/250], Loss: 0.6659085154533386\n", - "Epoch [7/10], Step [220/250], Loss: 0.6364413499832153\n", - "Epoch [7/10], Step [230/250], Loss: 0.878304123878479\n", - "Epoch [7/10], Step [240/250], Loss: 0.7139410972595215\n", - "Epoch [7/10], Step [250/250], Loss: 0.6852972507476807\n", - "Epoch [8/10], Step [10/250], Loss: 1.0263853073120117\n", - "Epoch [8/10], Step [20/250], Loss: 0.7559791803359985\n", - "Epoch [8/10], Step [30/250], Loss: 0.6709325313568115\n", - "Epoch [8/10], Step [40/250], Loss: 0.5146634578704834\n", - "Epoch [8/10], Step [50/250], Loss: 0.6418485641479492\n", - "Epoch [8/10], Step [60/250], Loss: 0.72318035364151\n", - "Epoch [8/10], Step [70/250], Loss: 0.7116968631744385\n", - "Epoch [8/10], Step [80/250], Loss: 0.7035868763923645\n", - "Epoch [8/10], Step [90/250], Loss: 0.6085933446884155\n", - "Epoch [8/10], Step [100/250], Loss: 0.5128545761108398\n", - "Epoch [8/10], Step [110/250], Loss: 0.6380510330200195\n", - "Epoch [8/10], Step [120/250], Loss: 0.4963105320930481\n", - "Epoch [8/10], Step [130/250], Loss: 0.6693160533905029\n", - "Epoch [8/10], Step [140/250], Loss: 0.6602588891983032\n", - "Epoch [8/10], Step [150/250], Loss: 0.8440876007080078\n", - "Epoch [8/10], Step [160/250], Loss: 0.7596740126609802\n", - "Epoch [8/10], Step [170/250], Loss: 0.695992112159729\n", - "Epoch [8/10], Step [180/250], Loss: 0.6737014651298523\n", - "Epoch [8/10], Step [190/250], Loss: 0.6722623705863953\n", - "Epoch [8/10], Step [200/250], Loss: 0.5857406854629517\n", - "Epoch [8/10], Step [210/250], Loss: 0.9563039541244507\n", - "Epoch [8/10], Step [220/250], Loss: 0.7375826835632324\n", - "Epoch [8/10], Step [230/250], Loss: 0.8751094341278076\n", - "Epoch [8/10], Step [240/250], Loss: 0.7180076837539673\n", - "Epoch [8/10], Step [250/250], Loss: 0.6384711861610413\n", - "Epoch [9/10], Step [10/250], Loss: 0.6789698004722595\n", - "Epoch [9/10], Step [20/250], Loss: 0.6645065546035767\n", - "Epoch [9/10], Step [30/250], Loss: 0.6996726989746094\n", - "Epoch [9/10], Step [40/250], Loss: 0.7402397394180298\n", - "Epoch [9/10], Step [50/250], Loss: 0.6388964653015137\n", - "Epoch [9/10], Step [60/250], Loss: 0.9401450753211975\n", - "Epoch [9/10], Step [70/250], Loss: 0.6708970665931702\n", - "Epoch [9/10], Step [80/250], Loss: 0.728550136089325\n", - "Epoch [9/10], Step [90/250], Loss: 0.7362596988677979\n", - "Epoch [9/10], Step [100/250], Loss: 0.7750495672225952\n", - "Epoch [9/10], Step [110/250], Loss: 0.807244062423706\n", - "Epoch [9/10], Step [120/250], Loss: 0.754521369934082\n", - "Epoch [9/10], Step [130/250], Loss: 0.5469345450401306\n", - "Epoch [9/10], Step [140/250], Loss: 0.8965460062026978\n", - "Epoch [9/10], Step [150/250], Loss: 0.7952369451522827\n", - "Epoch [9/10], Step [160/250], Loss: 0.6263578534126282\n", - "Epoch [9/10], Step [170/250], Loss: 0.5788871049880981\n", - "Epoch [9/10], Step [180/250], Loss: 0.7363749146461487\n", - "Epoch [9/10], Step [190/250], Loss: 0.7322844862937927\n", - "Epoch [9/10], Step [200/250], Loss: 0.6707043051719666\n", - "Epoch [9/10], Step [210/250], Loss: 0.7251213192939758\n", - "Epoch [9/10], Step [220/250], Loss: 0.6435517072677612\n", - "Epoch [9/10], Step [230/250], Loss: 0.534774124622345\n", - "Epoch [9/10], Step [240/250], Loss: 0.6989405751228333\n", - "Epoch [9/10], Step [250/250], Loss: 0.7413943409919739\n", - "Epoch [10/10], Step [10/250], Loss: 0.6014090776443481\n", - "Epoch [10/10], Step [20/250], Loss: 0.8173813819885254\n", - "Epoch [10/10], Step [30/250], Loss: 0.8984671235084534\n", - "Epoch [10/10], Step [40/250], Loss: 0.6354056000709534\n", - "Epoch [10/10], Step [50/250], Loss: 0.7964866757392883\n", - "Epoch [10/10], Step [60/250], Loss: 0.7849454879760742\n", - "Epoch [10/10], Step [70/250], Loss: 0.5637381076812744\n", - "Epoch [10/10], Step [80/250], Loss: 0.7669687271118164\n", - "Epoch [10/10], Step [90/250], Loss: 0.6140038371086121\n", - "Epoch [10/10], Step [100/250], Loss: 0.7134058475494385\n", - "Epoch [10/10], Step [110/250], Loss: 0.6768066883087158\n", - "Epoch [10/10], Step [120/250], Loss: 0.6304113268852234\n", - "Epoch [10/10], Step [130/250], Loss: 0.7426990866661072\n", - "Epoch [10/10], Step [140/250], Loss: 0.7469097971916199\n", - "Epoch [10/10], Step [150/250], Loss: 0.7591947913169861\n", - "Epoch [10/10], Step [160/250], Loss: 0.7327935099601746\n", - "Epoch [10/10], Step [170/250], Loss: 0.8590223789215088\n", - "Epoch [10/10], Step [180/250], Loss: 0.6994909644126892\n", - "Epoch [10/10], Step [190/250], Loss: 0.8262240886688232\n", - "Epoch [10/10], Step [200/250], Loss: 0.6071692109107971\n", - "Epoch [10/10], Step [210/250], Loss: 0.915013313293457\n", - "Epoch [10/10], Step [220/250], Loss: 0.8758894205093384\n", - "Epoch [10/10], Step [230/250], Loss: 0.6473208665847778\n", - "Epoch [10/10], Step [240/250], Loss: 0.6843898296356201\n", - "Epoch [10/10], Step [250/250], Loss: 0.6645953059196472\n", - "\n", - "Training job (train1u1it512gqg) succeeded, you can check the logs/metrics/output in the console:\n", - "https://pai.console.aliyun.com/?regionId=cn-hangzhou&workspaceId=58670#/training/jobs/train1u1it512gqg\n" - ] - } - ], - "source": [ - "from pai.estimator import Estimator\n", - "from pai.image import retrieve\n", - "\n", - "\n", - "epochs = 10\n", - "\n", - "\n", - "# 训练作业默认会挂载一个OSS Bucket路径到 /ml/output/checkpoints/\n", - "est = Estimator(\n", - " command=\"python train.py --epochs {}\".format(epochs),\n", - " source_dir=\"./train_src/\",\n", - " image_uri=retrieve(\"PyTorch\", \"latest\").image_uri,\n", - " instance_type=\"ecs.c6.large\",\n", - " base_job_name=\"torch_checkpoint\",\n", - ")\n", - "\n", - "est.fit()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# 训练作业的checkpoints目录\n", - "print(est.checkpoints_data())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "以上训练作业对训练数据做了10次迭代,通过使用checkpoint,我们可以在原先模型的基础上继续训练,例如使用训练数据继续迭代20次迭代。\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [ - "keep_output" - ] - }, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "1465353ea22d4b9a86f7b5b892f23471", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Uploading file: /var/folders/hc/5w4bg25j1ns2mm0yb06zzzbh0000gp/T/tmpshzpdx_z/source.tar.gz: 0%| | 0…" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "View the job detail by accessing the console URI: https://pai.console.aliyun.com/?regionId=cn-hangzhou&workspaceId=58670#/training/jobs/trainu90lc57j1vm\n", - "TrainingJob launch starting\n", - "MAX_PARALLELISM=0\n", - "C_INCLUDE_PATH=/home/pai/include\n", - "KUBERNETES_SERVICE_PORT=443\n", - "KUBERNETES_PORT=tcp://10.192.0.1:443\n", - "LANGUAGE=en_US.UTF-8\n", - "PIP_TRUSTED_HOST=mirrors.cloud.aliyuncs.com\n", - "MASTER_ADDR=trainu90lc57j1vm-master-0\n", - "HOSTNAME=trainu90lc57j1vm-master-0\n", - "LD_LIBRARY_PATH=:/lib/x86_64-linux-gnu:/home/pai/lib:/home/pai/jre/lib/amd64/server\n", - "MASTER_PORT=23456\n", - "HOME=/root\n", - "PAI_USER_ARGS=\n", - "PYTHONUNBUFFERED=0\n", - "PAI_OUTPUT_CHECKPOINTS=/ml/output/checkpoints/\n", - "PAI_CONFIG_DIR=/ml/input/config/\n", - "WORLD_SIZE=1\n", - "REGION_ID=cn-hangzhou\n", - "CPLUS_INCLUDE_PATH=/home/pai/include\n", - "RANK=0\n", - "OPAL_PREFIX=/home/pai/\n", - "PAI_TRAINING_JOB_ID=trainu90lc57j1vm\n", - "TERM=xterm-color\n", - "KUBERNETES_PORT_443_TCP_ADDR=10.192.0.1\n", - "PAI_OUTPUT_MODEL=/ml/output/model/\n", - "ELASTIC_TRAINING_ENABLED=false\n", - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/home/pai/bin:/home/pai/hadoop/bin\n", - "PIP_INDEX_URL=https://mirrors.cloud.aliyuncs.com/pypi/simple\n", - "KUBERNETES_PORT_443_TCP_PORT=443\n", - "KUBERNETES_PORT_443_TCP_PROTO=tcp\n", - "LANG=en_US.UTF-8\n", - "PAI_TRAINING_USE_ECI=true\n", - "aliyun_logs_containerType_tags=containerType=Algorithm\n", - "KUBERNETES_PORT_443_TCP=tcp://10.192.0.1:443\n", - "KUBERNETES_SERVICE_PORT_HTTPS=443\n", - "ELASTIC_INFERENCE_ENABLED=false\n", - "LC_ALL=en_US.UTF-8\n", - "JAVA_HOME=/home/pai\n", - "KUBERNETES_SERVICE_HOST=10.192.0.1\n", - "PWD=/\n", - "PAI_HPS={}\n", - "TZ=UTC\n", - "HADOOP_HOME=/home/pai/hadoop\n", - "PAI_OUTPUT_LOGS=/ml/output/logs/\n", - "aliyun_logs_trainingJobId_tags=trainingJobId=trainu90lc57j1vm\n", - "PAI_ODPS_CREDENTIAL=/ml/input/credential/odps.json\n", - "PAI_WORKING_DIR=/ml/usercode/\n", - "Change to Working Directory, /ml/usercode/\n", - "User program launching\n", - "-----------------------------------------------------------------\n", - "30\n", - "Epoch [11/30], Step [10/250], Loss: 0.678845226764679\n", - "Epoch [11/30], Step [20/250], Loss: 0.6292213201522827\n", - "Epoch [11/30], Step [30/250], Loss: 0.6856911182403564\n", - "Epoch [11/30], Step [40/250], Loss: 0.6147192716598511\n", - "Epoch [11/30], Step [50/250], Loss: 0.7846511602401733\n", - "Epoch [11/30], Step [60/250], Loss: 0.6719473004341125\n", - "Epoch [11/30], Step [70/250], Loss: 0.8227031826972961\n", - "Epoch [11/30], Step [80/250], Loss: 0.7861220836639404\n", - "Epoch [11/30], Step [90/250], Loss: 0.7436649203300476\n", - "Epoch [11/30], Step [100/250], Loss: 0.8053247928619385\n", - "Epoch [11/30], Step [110/250], Loss: 0.716484546661377\n", - "Epoch [11/30], Step [120/250], Loss: 0.6527263522148132\n", - "Epoch [11/30], Step [130/250], Loss: 0.7980918884277344\n", - "Epoch [11/30], Step [140/250], Loss: 0.6761615872383118\n", - "Epoch [11/30], Step [150/250], Loss: 0.8030520081520081\n", - "Epoch [11/30], Step [160/250], Loss: 0.6580255627632141\n", - "Epoch [11/30], Step [170/250], Loss: 0.7671869993209839\n", - "Epoch [11/30], Step [180/250], Loss: 0.6622000932693481\n", - "Epoch [11/30], Step [190/250], Loss: 0.747247576713562\n", - "Epoch [11/30], Step [200/250], Loss: 0.705307126045227\n", - "Epoch [11/30], Step [210/250], Loss: 0.6516950130462646\n", - "Epoch [11/30], Step [220/250], Loss: 0.6065223217010498\n", - "Epoch [11/30], Step [230/250], Loss: 0.6885045766830444\n", - "Epoch [11/30], Step [240/250], Loss: 0.7392936944961548\n", - "Epoch [11/30], Step [250/250], Loss: 0.6803852319717407\n", - "Epoch [12/30], Step [10/250], Loss: 0.8813486695289612\n", - "Epoch [12/30], Step [20/250], Loss: 0.7780698537826538\n", - "Epoch [12/30], Step [30/250], Loss: 0.7158650159835815\n", - "Epoch [12/30], Step [40/250], Loss: 0.5826153755187988\n", - "Epoch [12/30], Step [50/250], Loss: 0.6013429760932922\n", - "Epoch [12/30], Step [60/250], Loss: 0.7084614634513855\n", - "Epoch [12/30], Step [70/250], Loss: 0.6825753450393677\n", - "Epoch [12/30], Step [80/250], Loss: 0.6074261665344238\n", - "Epoch [12/30], Step [90/250], Loss: 0.8619674444198608\n", - "Epoch [12/30], Step [100/250], Loss: 0.6013283729553223\n", - "Epoch [12/30], Step [110/250], Loss: 0.6808617115020752\n", - "Epoch [12/30], Step [120/250], Loss: 0.6765388250350952\n", - "Epoch [12/30], Step [130/250], Loss: 0.7072106599807739\n", - "Epoch [12/30], Step [140/250], Loss: 0.6905199289321899\n", - "Epoch [12/30], Step [150/250], Loss: 0.6942532062530518\n", - "Epoch [12/30], Step [160/250], Loss: 0.7181805968284607\n", - "Epoch [12/30], Step [170/250], Loss: 0.6357207298278809\n", - "Epoch [12/30], Step [180/250], Loss: 0.6719130277633667\n", - "Epoch [12/30], Step [190/250], Loss: 0.7218160629272461\n", - "Epoch [12/30], Step [200/250], Loss: 0.7158771753311157\n", - "Epoch [12/30], Step [210/250], Loss: 0.7585588693618774\n", - "Epoch [12/30], Step [220/250], Loss: 0.8121419548988342\n", - "Epoch [12/30], Step [230/250], Loss: 0.7744668126106262\n", - "Epoch [12/30], Step [240/250], Loss: 0.7164073586463928\n", - "Epoch [12/30], Step [250/250], Loss: 0.5488151907920837\n", - "Epoch [13/30], Step [10/250], Loss: 0.7662173509597778\n", - "Epoch [13/30], Step [20/250], Loss: 0.7802825570106506\n", - "Epoch [13/30], Step [30/250], Loss: 0.7456352114677429\n", - "Epoch [13/30], Step [40/250], Loss: 0.6143842935562134\n", - "Epoch [13/30], Step [50/250], Loss: 0.7393404245376587\n", - "Epoch [13/30], Step [60/250], Loss: 0.6536136865615845\n", - "Epoch [13/30], Step [70/250], Loss: 0.7647539377212524\n", - "Epoch [13/30], Step [80/250], Loss: 0.6415259838104248\n", - "Epoch [13/30], Step [90/250], Loss: 0.8065975904464722\n", - "Epoch [13/30], Step [100/250], Loss: 0.654565155506134\n", - "Epoch [13/30], Step [110/250], Loss: 0.6512014865875244\n", - "Epoch [13/30], Step [120/250], Loss: 0.6851429343223572\n", - "Epoch [13/30], Step [130/250], Loss: 0.7639355659484863\n", - "Epoch [13/30], Step [140/250], Loss: 0.7886079549789429\n", - "Epoch [13/30], Step [150/250], Loss: 0.677024245262146\n", - "Epoch [13/30], Step [160/250], Loss: 0.6869807243347168\n", - "Epoch [13/30], Step [170/250], Loss: 0.7076682448387146\n", - "Epoch [13/30], Step [180/250], Loss: 0.6720783710479736\n", - "Epoch [13/30], Step [190/250], Loss: 0.6578226685523987\n", - "Epoch [13/30], Step [200/250], Loss: 0.6924010515213013\n", - "Epoch [13/30], Step [210/250], Loss: 0.8084946870803833\n", - "Epoch [13/30], Step [220/250], Loss: 0.7015032768249512\n", - "Epoch [13/30], Step [230/250], Loss: 0.6897311210632324\n", - "Epoch [13/30], Step [240/250], Loss: 0.7233715653419495\n", - "Epoch [13/30], Step [250/250], Loss: 0.82469242811203\n", - "Epoch [14/30], Step [10/250], Loss: 0.7118442058563232\n", - "Epoch [14/30], Step [20/250], Loss: 0.66881263256073\n", - "Epoch [14/30], Step [30/250], Loss: 0.6966590881347656\n", - "Epoch [14/30], Step [40/250], Loss: 0.8390185236930847\n", - "Epoch [14/30], Step [50/250], Loss: 0.7978378534317017\n", - "Epoch [14/30], Step [60/250], Loss: 0.6207278966903687\n", - "Epoch [14/30], Step [70/250], Loss: 0.6512827277183533\n", - "Epoch [14/30], Step [80/250], Loss: 0.6850301027297974\n", - "Epoch [14/30], Step [90/250], Loss: 0.628646194934845\n", - "Epoch [14/30], Step [100/250], Loss: 0.6093996167182922\n", - "Epoch [14/30], Step [110/250], Loss: 0.7588788866996765\n", - "Epoch [14/30], Step [120/250], Loss: 0.6795099377632141\n", - "Epoch [14/30], Step [130/250], Loss: 0.6357916593551636\n", - "Epoch [14/30], Step [140/250], Loss: 0.7358158826828003\n", - "Epoch [14/30], Step [150/250], Loss: 0.6896149516105652\n", - "Epoch [14/30], Step [160/250], Loss: 0.6862155199050903\n", - "Epoch [14/30], Step [170/250], Loss: 0.659408688545227\n", - "Epoch [14/30], Step [180/250], Loss: 0.717597246170044\n", - "Epoch [14/30], Step [190/250], Loss: 0.6779205203056335\n", - "Epoch [14/30], Step [200/250], Loss: 0.6569654941558838\n", - "Epoch [14/30], Step [210/250], Loss: 0.6521044373512268\n", - "Epoch [14/30], Step [220/250], Loss: 0.5803452134132385\n", - "Epoch [14/30], Step [230/250], Loss: 0.6112836599349976\n", - "Epoch [14/30], Step [240/250], Loss: 0.6311125755310059\n", - "Epoch [14/30], Step [250/250], Loss: 0.6427040696144104\n", - "Epoch [15/30], Step [10/250], Loss: 0.7193827629089355\n", - "Epoch [15/30], Step [20/250], Loss: 0.6781796216964722\n", - "Epoch [15/30], Step [30/250], Loss: 0.7042354345321655\n", - "Epoch [15/30], Step [40/250], Loss: 0.6776638627052307\n", - "Epoch [15/30], Step [50/250], Loss: 0.6593765020370483\n", - "Epoch [15/30], Step [60/250], Loss: 0.6749820113182068\n", - "Epoch [15/30], Step [70/250], Loss: 0.6199281811714172\n", - "Epoch [15/30], Step [80/250], Loss: 0.6898410320281982\n", - "Epoch [15/30], Step [90/250], Loss: 0.6938673257827759\n", - "Epoch [15/30], Step [100/250], Loss: 0.6369883418083191\n", - "Epoch [15/30], Step [110/250], Loss: 0.6758348345756531\n", - "Epoch [15/30], Step [120/250], Loss: 0.7379288673400879\n", - "Epoch [15/30], Step [130/250], Loss: 0.6447997689247131\n", - "Epoch [15/30], Step [140/250], Loss: 0.6910532712936401\n", - "Epoch [15/30], Step [150/250], Loss: 0.7426170110702515\n", - "Epoch [15/30], Step [160/250], Loss: 0.6422319412231445\n", - "Epoch [15/30], Step [170/250], Loss: 0.5789802670478821\n", - "Epoch [15/30], Step [180/250], Loss: 0.7434327602386475\n", - "Epoch [15/30], Step [190/250], Loss: 0.6754781007766724\n", - "Epoch [15/30], Step [200/250], Loss: 0.5865523815155029\n", - "Epoch [15/30], Step [210/250], Loss: 0.6548283696174622\n", - "Epoch [15/30], Step [220/250], Loss: 0.7495550513267517\n", - "Epoch [15/30], Step [230/250], Loss: 0.6538060903549194\n", - "Epoch [15/30], Step [240/250], Loss: 0.7314434051513672\n", - "Epoch [15/30], Step [250/250], Loss: 0.7135218381881714\n", - "Epoch [16/30], Step [10/250], Loss: 0.7383496761322021\n", - "Epoch [16/30], Step [20/250], Loss: 0.644036591053009\n", - "Epoch [16/30], Step [30/250], Loss: 0.6101108193397522\n", - "Epoch [16/30], Step [40/250], Loss: 0.7390760779380798\n", - "Epoch [16/30], Step [50/250], Loss: 0.6870918273925781\n", - "Epoch [16/30], Step [60/250], Loss: 0.6894906759262085\n", - "Epoch [16/30], Step [70/250], Loss: 0.7674188017845154\n", - "Epoch [16/30], Step [80/250], Loss: 0.7476275563240051\n", - "Epoch [16/30], Step [90/250], Loss: 0.7009009718894958\n", - "Epoch [16/30], Step [100/250], Loss: 0.6951045989990234\n", - "Epoch [16/30], Step [110/250], Loss: 0.7023512721061707\n", - "Epoch [16/30], Step [120/250], Loss: 0.6900476217269897\n", - "Epoch [16/30], Step [130/250], Loss: 0.7070642709732056\n", - "Epoch [16/30], Step [140/250], Loss: 0.6627304553985596\n", - "Epoch [16/30], Step [150/250], Loss: 0.676548182964325\n", - "Epoch [16/30], Step [160/250], Loss: 0.7038763761520386\n", - "Epoch [16/30], Step [170/250], Loss: 0.6916297078132629\n", - "Epoch [16/30], Step [180/250], Loss: 0.7028259634971619\n", - "Epoch [16/30], Step [190/250], Loss: 0.6524210572242737\n", - "Epoch [16/30], Step [200/250], Loss: 0.7346513867378235\n", - "Epoch [16/30], Step [210/250], Loss: 0.612514317035675\n", - "Epoch [16/30], Step [220/250], Loss: 0.7455917596817017\n", - "Epoch [16/30], Step [230/250], Loss: 0.747292160987854\n", - "Epoch [16/30], Step [240/250], Loss: 0.7447240352630615\n", - "Epoch [16/30], Step [250/250], Loss: 0.6769564747810364\n", - "Epoch [17/30], Step [10/250], Loss: 0.7425077557563782\n", - "Epoch [17/30], Step [20/250], Loss: 0.6944329738616943\n", - "Epoch [17/30], Step [30/250], Loss: 0.6961978673934937\n", - "Epoch [17/30], Step [40/250], Loss: 0.6465986967086792\n", - "Epoch [17/30], Step [50/250], Loss: 0.714703381061554\n", - "Epoch [17/30], Step [60/250], Loss: 0.5930614471435547\n", - "Epoch [17/30], Step [70/250], Loss: 0.6468428373336792\n", - "Epoch [17/30], Step [80/250], Loss: 0.686537504196167\n", - "Epoch [17/30], Step [90/250], Loss: 0.7371711730957031\n", - "Epoch [17/30], Step [100/250], Loss: 0.7700399160385132\n", - "Epoch [17/30], Step [110/250], Loss: 0.7529278993606567\n", - "Epoch [17/30], Step [120/250], Loss: 0.7036042213439941\n", - "Epoch [17/30], Step [130/250], Loss: 0.7871543765068054\n", - "Epoch [17/30], Step [140/250], Loss: 0.6956086158752441\n", - "Epoch [17/30], Step [150/250], Loss: 0.7426921725273132\n", - "Epoch [17/30], Step [160/250], Loss: 0.7222756743431091\n", - "Epoch [17/30], Step [170/250], Loss: 0.6826121807098389\n", - "Epoch [17/30], Step [180/250], Loss: 0.6970388293266296\n", - "Epoch [17/30], Step [190/250], Loss: 0.7087472677230835\n", - "Epoch [17/30], Step [200/250], Loss: 0.6320711374282837\n", - "Epoch [17/30], Step [210/250], Loss: 0.7280303835868835\n", - "Epoch [17/30], Step [220/250], Loss: 0.6934517621994019\n", - "Epoch [17/30], Step [230/250], Loss: 0.7071420550346375\n", - "Epoch [17/30], Step [240/250], Loss: 0.6856362223625183\n", - "Epoch [17/30], Step [250/250], Loss: 0.6945990324020386\n", - "Epoch [18/30], Step [10/250], Loss: 0.6465855240821838\n", - "Epoch [18/30], Step [20/250], Loss: 0.7086865901947021\n", - "Epoch [18/30], Step [30/250], Loss: 0.6256162524223328\n", - "Epoch [18/30], Step [40/250], Loss: 0.6532611846923828\n", - "Epoch [18/30], Step [50/250], Loss: 0.6484596729278564\n", - "Epoch [18/30], Step [60/250], Loss: 0.6955176591873169\n", - "Epoch [18/30], Step [70/250], Loss: 0.6615030765533447\n", - "Epoch [18/30], Step [80/250], Loss: 0.7038217186927795\n", - "Epoch [18/30], Step [90/250], Loss: 0.6943345069885254\n", - "Epoch [18/30], Step [100/250], Loss: 0.7004052996635437\n", - "Epoch [18/30], Step [110/250], Loss: 0.7458634972572327\n", - "Epoch [18/30], Step [120/250], Loss: 0.6851629614830017\n", - "Epoch [18/30], Step [130/250], Loss: 0.682853102684021\n", - "Epoch [18/30], Step [140/250], Loss: 0.6481672525405884\n", - "Epoch [18/30], Step [150/250], Loss: 0.7038549780845642\n", - "Epoch [18/30], Step [160/250], Loss: 0.6995554566383362\n", - "Epoch [18/30], Step [170/250], Loss: 0.6800370216369629\n", - "Epoch [18/30], Step [180/250], Loss: 0.6488386392593384\n", - "Epoch [18/30], Step [190/250], Loss: 0.7000787854194641\n", - "Epoch [18/30], Step [200/250], Loss: 0.7428950071334839\n", - "Epoch [18/30], Step [210/250], Loss: 0.6872988343238831\n", - "Epoch [18/30], Step [220/250], Loss: 0.6482336521148682\n", - "Epoch [18/30], Step [230/250], Loss: 0.6626957058906555\n", - "Epoch [18/30], Step [240/250], Loss: 0.6778802275657654\n", - "Epoch [18/30], Step [250/250], Loss: 0.7027387022972107\n", - "Epoch [19/30], Step [10/250], Loss: 0.6812503933906555\n", - "Epoch [19/30], Step [20/250], Loss: 0.6751934289932251\n", - "Epoch [19/30], Step [30/250], Loss: 0.6624279618263245\n", - "Epoch [19/30], Step [40/250], Loss: 0.6787773966789246\n", - "Epoch [19/30], Step [50/250], Loss: 0.7765601873397827\n", - "Epoch [19/30], Step [60/250], Loss: 0.6592363119125366\n", - "Epoch [19/30], Step [70/250], Loss: 0.7038179039955139\n", - "Epoch [19/30], Step [80/250], Loss: 0.7358537316322327\n", - "Epoch [19/30], Step [90/250], Loss: 0.708828330039978\n", - "Epoch [19/30], Step [100/250], Loss: 0.7642552852630615\n", - "Epoch [19/30], Step [110/250], Loss: 0.7605912089347839\n", - "Epoch [19/30], Step [120/250], Loss: 0.6976773738861084\n", - "Epoch [19/30], Step [130/250], Loss: 0.6766220331192017\n", - "Epoch [19/30], Step [140/250], Loss: 0.7171740531921387\n", - "Epoch [19/30], Step [150/250], Loss: 0.6521143913269043\n", - "Epoch [19/30], Step [160/250], Loss: 0.6554864645004272\n", - "Epoch [19/30], Step [170/250], Loss: 0.6797289848327637\n", - "Epoch [19/30], Step [180/250], Loss: 0.6546230316162109\n", - "Epoch [19/30], Step [190/250], Loss: 0.6951708197593689\n", - "Epoch [19/30], Step [200/250], Loss: 0.7692861557006836\n", - "Epoch [19/30], Step [210/250], Loss: 0.6987319588661194\n", - "Epoch [19/30], Step [220/250], Loss: 0.7281709909439087\n", - "Epoch [19/30], Step [230/250], Loss: 0.6981549263000488\n", - "Epoch [19/30], Step [240/250], Loss: 0.6613932847976685\n", - "Epoch [19/30], Step [250/250], Loss: 0.6515719890594482\n", - "Epoch [20/30], Step [10/250], Loss: 0.683667004108429\n", - "Epoch [20/30], Step [20/250], Loss: 0.6330690383911133\n", - "Epoch [20/30], Step [30/250], Loss: 0.6992578506469727\n", - "Epoch [20/30], Step [40/250], Loss: 0.7081963419914246\n", - "Epoch [20/30], Step [50/250], Loss: 0.7147829532623291\n", - "Epoch [20/30], Step [60/250], Loss: 0.6547238826751709\n", - "Epoch [20/30], Step [70/250], Loss: 0.627391517162323\n", - "Epoch [20/30], Step [80/250], Loss: 0.6972628831863403\n", - "Epoch [20/30], Step [90/250], Loss: 0.6500757932662964\n", - "Epoch [20/30], Step [100/250], Loss: 0.7282431125640869\n", - "Epoch [20/30], Step [110/250], Loss: 0.6599644422531128\n", - "Epoch [20/30], Step [120/250], Loss: 0.691277265548706\n", - "Epoch [20/30], Step [130/250], Loss: 0.6712023019790649\n", - "Epoch [20/30], Step [140/250], Loss: 0.6875613927841187\n", - "Epoch [20/30], Step [150/250], Loss: 0.6852554082870483\n", - "Epoch [20/30], Step [160/250], Loss: 0.7059615850448608\n", - "Epoch [20/30], Step [170/250], Loss: 0.7474350333213806\n", - "Epoch [20/30], Step [180/250], Loss: 0.6700282096862793\n", - "Epoch [20/30], Step [190/250], Loss: 0.7267058491706848\n", - "Epoch [20/30], Step [200/250], Loss: 0.6795942783355713\n", - "Epoch [20/30], Step [210/250], Loss: 0.7355214953422546\n", - "Epoch [20/30], Step [220/250], Loss: 0.7097989320755005\n", - "Epoch [20/30], Step [230/250], Loss: 0.6741981506347656\n", - "Epoch [20/30], Step [240/250], Loss: 0.7197920680046082\n", - "Epoch [20/30], Step [250/250], Loss: 0.6666856408119202\n", - "Epoch [21/30], Step [10/250], Loss: 0.6850540637969971\n", - "Epoch [21/30], Step [20/250], Loss: 0.6577891111373901\n", - "Epoch [21/30], Step [30/250], Loss: 0.7145082354545593\n", - "Epoch [21/30], Step [40/250], Loss: 0.6782787442207336\n", - "Epoch [21/30], Step [50/250], Loss: 0.7092875242233276\n", - "Epoch [21/30], Step [60/250], Loss: 0.6552045941352844\n", - "Epoch [21/30], Step [70/250], Loss: 0.665422260761261\n", - "Epoch [21/30], Step [80/250], Loss: 0.7131606340408325\n", - "Epoch [21/30], Step [90/250], Loss: 0.6851215362548828\n", - "Epoch [21/30], Step [100/250], Loss: 0.7093809843063354\n", - "Epoch [21/30], Step [110/250], Loss: 0.6839103698730469\n", - "Epoch [21/30], Step [120/250], Loss: 0.6863808035850525\n", - "Epoch [21/30], Step [130/250], Loss: 0.6923962831497192\n", - "Epoch [21/30], Step [140/250], Loss: 0.7143585085868835\n", - "Epoch [21/30], Step [150/250], Loss: 0.7165741324424744\n", - "Epoch [21/30], Step [160/250], Loss: 0.7011140584945679\n", - "Epoch [21/30], Step [170/250], Loss: 0.7145777344703674\n", - "Epoch [21/30], Step [180/250], Loss: 0.6781455278396606\n", - "Epoch [21/30], Step [190/250], Loss: 0.704175591468811\n", - "Epoch [21/30], Step [200/250], Loss: 0.6643280982971191\n", - "Epoch [21/30], Step [210/250], Loss: 0.7143128514289856\n", - "Epoch [21/30], Step [220/250], Loss: 0.7122169137001038\n", - "Epoch [21/30], Step [230/250], Loss: 0.7329443693161011\n", - "Epoch [21/30], Step [240/250], Loss: 0.7038950324058533\n", - "Epoch [21/30], Step [250/250], Loss: 0.683397114276886\n", - "Epoch [22/30], Step [10/250], Loss: 0.6960069537162781\n", - "Epoch [22/30], Step [20/250], Loss: 0.6595947742462158\n", - "Epoch [22/30], Step [30/250], Loss: 0.7287018895149231\n", - "Epoch [22/30], Step [40/250], Loss: 0.7046036720275879\n", - "Epoch [22/30], Step [50/250], Loss: 0.7062811255455017\n", - "Epoch [22/30], Step [60/250], Loss: 0.7442296743392944\n", - "Epoch [22/30], Step [70/250], Loss: 0.6482053399085999\n", - "Epoch [22/30], Step [80/250], Loss: 0.722833514213562\n", - "Epoch [22/30], Step [90/250], Loss: 0.6747336387634277\n", - "Epoch [22/30], Step [100/250], Loss: 0.7139792442321777\n", - "Epoch [22/30], Step [110/250], Loss: 0.680081844329834\n", - "Epoch [22/30], Step [120/250], Loss: 0.686549186706543\n", - "Epoch [22/30], Step [130/250], Loss: 0.6854720115661621\n", - "Epoch [22/30], Step [140/250], Loss: 0.6525530815124512\n", - "Epoch [22/30], Step [150/250], Loss: 0.6676555871963501\n", - "Epoch [22/30], Step [160/250], Loss: 0.7014628052711487\n", - "Epoch [22/30], Step [170/250], Loss: 0.7186480760574341\n", - "Epoch [22/30], Step [180/250], Loss: 0.6748342514038086\n", - "Epoch [22/30], Step [190/250], Loss: 0.7034397125244141\n", - "Epoch [22/30], Step [200/250], Loss: 0.6637327075004578\n", - "Epoch [22/30], Step [210/250], Loss: 0.6852638125419617\n", - "Epoch [22/30], Step [220/250], Loss: 0.6631066203117371\n", - "Epoch [22/30], Step [230/250], Loss: 0.7248471975326538\n", - "Epoch [22/30], Step [240/250], Loss: 0.7282781004905701\n", - "Epoch [22/30], Step [250/250], Loss: 0.678613007068634\n", - "Epoch [23/30], Step [10/250], Loss: 0.6844161748886108\n", - "Epoch [23/30], Step [20/250], Loss: 0.6881325244903564\n", - "Epoch [23/30], Step [30/250], Loss: 0.6631232500076294\n", - "Epoch [23/30], Step [40/250], Loss: 0.7202731370925903\n", - "Epoch [23/30], Step [50/250], Loss: 0.6977999210357666\n", - "Epoch [23/30], Step [60/250], Loss: 0.7103397846221924\n", - "Epoch [23/30], Step [70/250], Loss: 0.6726264953613281\n", - "Epoch [23/30], Step [80/250], Loss: 0.6642501354217529\n", - "Epoch [23/30], Step [90/250], Loss: 0.7357184886932373\n", - "Epoch [23/30], Step [100/250], Loss: 0.7160366773605347\n", - "Epoch [23/30], Step [110/250], Loss: 0.6603021621704102\n", - "Epoch [23/30], Step [120/250], Loss: 0.6760040521621704\n", - "Epoch [23/30], Step [130/250], Loss: 0.696141242980957\n", - "Epoch [23/30], Step [140/250], Loss: 0.6645365357398987\n", - "Epoch [23/30], Step [150/250], Loss: 0.7011918425559998\n", - "Epoch [23/30], Step [160/250], Loss: 0.6758050322532654\n", - "Epoch [23/30], Step [170/250], Loss: 0.6683043837547302\n", - "Epoch [23/30], Step [180/250], Loss: 0.6827936172485352\n", - "Epoch [23/30], Step [190/250], Loss: 0.699557900428772\n", - "Epoch [23/30], Step [200/250], Loss: 0.6873543858528137\n", - "Epoch [23/30], Step [210/250], Loss: 0.6973046064376831\n", - "Epoch [23/30], Step [220/250], Loss: 0.6847941279411316\n", - "Epoch [23/30], Step [230/250], Loss: 0.686026930809021\n", - "Epoch [23/30], Step [240/250], Loss: 0.712138831615448\n", - "Epoch [23/30], Step [250/250], Loss: 0.6938803791999817\n", - "Epoch [24/30], Step [10/250], Loss: 0.6833834648132324\n", - "Epoch [24/30], Step [20/250], Loss: 0.7029370069503784\n", - "Epoch [24/30], Step [30/250], Loss: 0.6896952390670776\n", - "Epoch [24/30], Step [40/250], Loss: 0.6966062784194946\n", - "Epoch [24/30], Step [50/250], Loss: 0.6755800247192383\n", - "Epoch [24/30], Step [60/250], Loss: 0.6890952587127686\n", - "Epoch [24/30], Step [70/250], Loss: 0.6705589294433594\n", - "Epoch [24/30], Step [80/250], Loss: 0.7066176533699036\n", - "Epoch [24/30], Step [90/250], Loss: 0.758873701095581\n", - "Epoch [24/30], Step [100/250], Loss: 0.699566125869751\n", - "Epoch [24/30], Step [110/250], Loss: 0.7008506059646606\n", - "Epoch [24/30], Step [120/250], Loss: 0.686880350112915\n", - "Epoch [24/30], Step [130/250], Loss: 0.6831185817718506\n", - "Epoch [24/30], Step [140/250], Loss: 0.6989403963088989\n", - "Epoch [24/30], Step [150/250], Loss: 0.7022895812988281\n", - "Epoch [24/30], Step [160/250], Loss: 0.7047298550605774\n", - "Epoch [24/30], Step [170/250], Loss: 0.6803637742996216\n", - "Epoch [24/30], Step [180/250], Loss: 0.6698098182678223\n", - "Epoch [24/30], Step [190/250], Loss: 0.6965357661247253\n", - "Epoch [24/30], Step [200/250], Loss: 0.7183314561843872\n", - "Epoch [24/30], Step [210/250], Loss: 0.7083855271339417\n", - "Epoch [24/30], Step [220/250], Loss: 0.688880205154419\n", - "Epoch [24/30], Step [230/250], Loss: 0.6859614253044128\n", - "Epoch [24/30], Step [240/250], Loss: 0.6815621852874756\n", - "Epoch [24/30], Step [250/250], Loss: 0.7023071050643921\n", - "Epoch [25/30], Step [10/250], Loss: 0.6979001760482788\n", - "Epoch [25/30], Step [20/250], Loss: 0.6792093515396118\n", - "Epoch [25/30], Step [30/250], Loss: 0.7000377178192139\n", - "Epoch [25/30], Step [40/250], Loss: 0.6891401410102844\n", - "Epoch [25/30], Step [50/250], Loss: 0.6950706839561462\n", - "Epoch [25/30], Step [60/250], Loss: 0.6931962966918945\n", - "Epoch [25/30], Step [70/250], Loss: 0.6918748021125793\n", - "Epoch [25/30], Step [80/250], Loss: 0.7022840976715088\n", - "Epoch [25/30], Step [90/250], Loss: 0.7233110666275024\n", - "Epoch [25/30], Step [100/250], Loss: 0.6882573366165161\n", - "Epoch [25/30], Step [110/250], Loss: 0.6959525346755981\n", - "Epoch [25/30], Step [120/250], Loss: 0.6953780651092529\n", - "Epoch [25/30], Step [130/250], Loss: 0.7029913067817688\n", - "Epoch [25/30], Step [140/250], Loss: 0.7104859948158264\n", - "Epoch [25/30], Step [150/250], Loss: 0.6983399391174316\n", - "Epoch [25/30], Step [160/250], Loss: 0.6920713186264038\n", - "Epoch [25/30], Step [170/250], Loss: 0.7179511189460754\n", - "Epoch [25/30], Step [180/250], Loss: 0.6971415281295776\n", - "Epoch [25/30], Step [190/250], Loss: 0.7037041783332825\n", - "Epoch [25/30], Step [200/250], Loss: 0.6952695846557617\n", - "Epoch [25/30], Step [210/250], Loss: 0.7007227540016174\n", - "Epoch [25/30], Step [220/250], Loss: 0.686070442199707\n", - "Epoch [25/30], Step [230/250], Loss: 0.692324161529541\n", - "Epoch [25/30], Step [240/250], Loss: 0.6936407089233398\n", - "Epoch [25/30], Step [250/250], Loss: 0.6896817088127136\n", - "Epoch [26/30], Step [10/250], Loss: 0.7085744142532349\n", - "Epoch [26/30], Step [20/250], Loss: 0.6863793730735779\n", - "Epoch [26/30], Step [30/250], Loss: 0.6817866563796997\n", - "Epoch [26/30], Step [40/250], Loss: 0.7037662267684937\n", - "Epoch [26/30], Step [50/250], Loss: 0.7046667337417603\n", - "Epoch [26/30], Step [60/250], Loss: 0.6918007135391235\n", - "Epoch [26/30], Step [70/250], Loss: 0.713044285774231\n", - "Epoch [26/30], Step [80/250], Loss: 0.6832862496376038\n", - "Epoch [26/30], Step [90/250], Loss: 0.667504608631134\n", - "Epoch [26/30], Step [100/250], Loss: 0.6760569214820862\n", - "Epoch [26/30], Step [110/250], Loss: 0.707482099533081\n", - "Epoch [26/30], Step [120/250], Loss: 0.6977518200874329\n", - "Epoch [26/30], Step [130/250], Loss: 0.6955530047416687\n", - "Epoch [26/30], Step [140/250], Loss: 0.7124805450439453\n", - "Epoch [26/30], Step [150/250], Loss: 0.6924611330032349\n", - "Epoch [26/30], Step [160/250], Loss: 0.6965060234069824\n", - "Epoch [26/30], Step [170/250], Loss: 0.6868378520011902\n", - "Epoch [26/30], Step [180/250], Loss: 0.7103825807571411\n", - "Epoch [26/30], Step [190/250], Loss: 0.6711806654930115\n", - "Epoch [26/30], Step [200/250], Loss: 0.6948347091674805\n", - "Epoch [26/30], Step [210/250], Loss: 0.7058894634246826\n", - "Epoch [26/30], Step [220/250], Loss: 0.6947336196899414\n", - "Epoch [26/30], Step [230/250], Loss: 0.689943253993988\n", - "Epoch [26/30], Step [240/250], Loss: 0.6956008672714233\n", - "Epoch [26/30], Step [250/250], Loss: 0.6892440319061279\n", - "Epoch [27/30], Step [10/250], Loss: 0.6945648193359375\n", - "Epoch [27/30], Step [20/250], Loss: 0.697243332862854\n", - "Epoch [27/30], Step [30/250], Loss: 0.6995589137077332\n", - "Epoch [27/30], Step [40/250], Loss: 0.6961522698402405\n", - "Epoch [27/30], Step [50/250], Loss: 0.7141368389129639\n", - "Epoch [27/30], Step [60/250], Loss: 0.6883167028427124\n", - "Epoch [27/30], Step [70/250], Loss: 0.681597888469696\n", - "Epoch [27/30], Step [80/250], Loss: 0.6933290362358093\n", - "Epoch [27/30], Step [90/250], Loss: 0.6990853548049927\n", - "Epoch [27/30], Step [100/250], Loss: 0.6930828094482422\n", - "Epoch [27/30], Step [110/250], Loss: 0.6889819502830505\n", - "Epoch [27/30], Step [120/250], Loss: 0.6966762542724609\n", - "Epoch [27/30], Step [130/250], Loss: 0.7014245986938477\n", - "Epoch [27/30], Step [140/250], Loss: 0.7081984281539917\n", - "Epoch [27/30], Step [150/250], Loss: 0.6894259452819824\n", - "Epoch [27/30], Step [160/250], Loss: 0.695622444152832\n", - "Epoch [27/30], Step [170/250], Loss: 0.6961721181869507\n", - "Epoch [27/30], Step [180/250], Loss: 0.6897941827774048\n", - "Epoch [27/30], Step [190/250], Loss: 0.6890014410018921\n", - "Epoch [27/30], Step [200/250], Loss: 0.6775841116905212\n", - "Epoch [27/30], Step [210/250], Loss: 0.6889995336532593\n", - "Epoch [27/30], Step [220/250], Loss: 0.6887487769126892\n", - "Epoch [27/30], Step [230/250], Loss: 0.6713950037956238\n", - "Epoch [27/30], Step [240/250], Loss: 0.6815714836120605\n", - "Epoch [27/30], Step [250/250], Loss: 0.6999087333679199\n", - "Epoch [28/30], Step [10/250], Loss: 0.7005322575569153\n", - "Epoch [28/30], Step [20/250], Loss: 0.6854400634765625\n", - "Epoch [28/30], Step [30/250], Loss: 0.7016850113868713\n", - "Epoch [28/30], Step [40/250], Loss: 0.6971641182899475\n", - "Epoch [28/30], Step [50/250], Loss: 0.6831482648849487\n", - "Epoch [28/30], Step [60/250], Loss: 0.6957387924194336\n", - "Epoch [28/30], Step [70/250], Loss: 0.6991732716560364\n", - "Epoch [28/30], Step [80/250], Loss: 0.6832884550094604\n", - "Epoch [28/30], Step [90/250], Loss: 0.6862078309059143\n", - "Epoch [28/30], Step [100/250], Loss: 0.7001485824584961\n", - "Epoch [28/30], Step [110/250], Loss: 0.686698317527771\n", - "Epoch [28/30], Step [120/250], Loss: 0.6935960054397583\n", - "Epoch [28/30], Step [130/250], Loss: 0.6797569990158081\n", - "Epoch [28/30], Step [140/250], Loss: 0.6913435459136963\n", - "Epoch [28/30], Step [150/250], Loss: 0.7099695205688477\n", - "Epoch [28/30], Step [160/250], Loss: 0.6739814877510071\n", - "Epoch [28/30], Step [170/250], Loss: 0.691004753112793\n", - "Epoch [28/30], Step [180/250], Loss: 0.6871265172958374\n", - "Epoch [28/30], Step [190/250], Loss: 0.6769859790802002\n", - "Epoch [28/30], Step [200/250], Loss: 0.6753854751586914\n", - "Epoch [28/30], Step [210/250], Loss: 0.6798712015151978\n", - "Epoch [28/30], Step [220/250], Loss: 0.6959697008132935\n", - "Epoch [28/30], Step [230/250], Loss: 0.6912880539894104\n", - "Epoch [28/30], Step [240/250], Loss: 0.7011526823043823\n", - "Epoch [28/30], Step [250/250], Loss: 0.6955965757369995\n", - "Epoch [29/30], Step [10/250], Loss: 0.700312077999115\n", - "Epoch [29/30], Step [20/250], Loss: 0.688980758190155\n", - "Epoch [29/30], Step [30/250], Loss: 0.687660813331604\n", - "Epoch [29/30], Step [40/250], Loss: 0.6973135471343994\n", - "Epoch [29/30], Step [50/250], Loss: 0.7041200995445251\n", - "Epoch [29/30], Step [60/250], Loss: 0.6702690720558167\n", - "Epoch [29/30], Step [70/250], Loss: 0.695311427116394\n", - "Epoch [29/30], Step [80/250], Loss: 0.7089749574661255\n", - "Epoch [29/30], Step [90/250], Loss: 0.6968417763710022\n", - "Epoch [29/30], Step [100/250], Loss: 0.6854453086853027\n", - "Epoch [29/30], Step [110/250], Loss: 0.6853547096252441\n", - "Epoch [29/30], Step [120/250], Loss: 0.6865882277488708\n", - "Epoch [29/30], Step [130/250], Loss: 0.6883337497711182\n", - "Epoch [29/30], Step [140/250], Loss: 0.705528974533081\n", - "Epoch [29/30], Step [150/250], Loss: 0.6866053938865662\n", - "Epoch [29/30], Step [160/250], Loss: 0.6900249123573303\n", - "Epoch [29/30], Step [170/250], Loss: 0.6984312534332275\n", - "Epoch [29/30], Step [180/250], Loss: 0.7001223564147949\n", - "Epoch [29/30], Step [190/250], Loss: 0.6993950605392456\n", - "Epoch [29/30], Step [200/250], Loss: 0.6955195069313049\n", - "Epoch [29/30], Step [210/250], Loss: 0.7174205183982849\n", - "Epoch [29/30], Step [220/250], Loss: 0.6770732998847961\n", - "Epoch [29/30], Step [230/250], Loss: 0.6760091781616211\n", - "Epoch [29/30], Step [240/250], Loss: 0.6769121885299683\n", - "Epoch [29/30], Step [250/250], Loss: 0.7050588130950928\n", - "Epoch [30/30], Step [10/250], Loss: 0.6745777130126953\n", - "Epoch [30/30], Step [20/250], Loss: 0.6881678104400635\n", - "Epoch [30/30], Step [30/250], Loss: 0.6794246435165405\n", - "Epoch [30/30], Step [40/250], Loss: 0.7122002840042114\n", - "Epoch [30/30], Step [50/250], Loss: 0.698681116104126\n", - "Epoch [30/30], Step [60/250], Loss: 0.7196323871612549\n", - "Epoch [30/30], Step [70/250], Loss: 0.6916103363037109\n", - "Epoch [30/30], Step [80/250], Loss: 0.6879148483276367\n", - "Epoch [30/30], Step [90/250], Loss: 0.7075177431106567\n", - "Epoch [30/30], Step [100/250], Loss: 0.6686447858810425\n", - "Epoch [30/30], Step [110/250], Loss: 0.7030155062675476\n", - "Epoch [30/30], Step [120/250], Loss: 0.7014066576957703\n", - "Epoch [30/30], Step [130/250], Loss: 0.7121413946151733\n", - "Epoch [30/30], Step [140/250], Loss: 0.6912719011306763\n", - "Epoch [30/30], Step [150/250], Loss: 0.6733638048171997\n", - "Epoch [30/30], Step [160/250], Loss: 0.7193289399147034\n", - "Epoch [30/30], Step [170/250], Loss: 0.6880522966384888\n", - "Epoch [30/30], Step [180/250], Loss: 0.7069193720817566\n", - "Epoch [30/30], Step [190/250], Loss: 0.6976951360702515\n", - "Epoch [30/30], Step [200/250], Loss: 0.6925494074821472\n", - "Epoch [30/30], Step [210/250], Loss: 0.6907849907875061\n", - "Epoch [30/30], Step [220/250], Loss: 0.6824172735214233\n", - "Epoch [30/30], Step [230/250], Loss: 0.6865588426589966\n", - "Epoch [30/30], Step [240/250], Loss: 0.6921617984771729\n", - "Epoch [30/30], Step [250/250], Loss: 0.6736024618148804\n", - "\n", - "Training job (trainu90lc57j1vm) succeeded, you can check the logs/metrics/output in the console:\n", - "https://pai.console.aliyun.com/?regionId=cn-hangzhou&workspaceId=58670#/training/jobs/trainu90lc57j1vm\n" - ] - } - ], - "source": [ - "from pai.estimator import Estimator\n", - "from pai.image import retrieve\n", - "\n", - "\n", - "# 训练数据的总迭代次数为30\n", - "epochs = 30\n", - "\n", - "resume_est = Estimator(\n", - " command=\"python train.py --epochs {}\".format(epochs),\n", - " source_dir=\"./train_src/\",\n", - " image_uri=retrieve(\"PyTorch\", \"latest\").image_uri,\n", - " instance_type=\"ecs.c6.large\",\n", - " # 使用上一个训练作业的checkpoints,相应的OSS Bucket路径会被挂载到 /ml/output/checkpoints 路径下\n", - " checkpoints_path=est.checkpoints_data(),\n", - " base_job_name=\"torch_resume_checkpoint\",\n", - ")\n", - "\n", - "resume_est.fit()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "通过训练作业日志的,我们可以看到训练作业加载了之前训练作业的checkpoint,在此基础上,从第11个epoch开始继续训练。" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 结语\n", - "\n", - "本文以`PyTorch`为示例,介绍了如何在PAI的训练作业中使用`checkpoint`:训练代码可以通过`/ml/output/checkpoints/`路径保存和加载`checkpoints`文件,`checkpoints`文件将被保存到OSS Bucket上。当用户使用其他的训练框架,例如`TensorFlow`、`HuggingFace transformers`、`ModelScope`等,也可以通过类似的方式在PAI的训练作业中使用`checkpoint`。\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "base", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.3" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/source/tutorial/framework.rst b/docs/source/tutorial/framework.rst deleted file mode 100644 index 48fbbb2..0000000 --- a/docs/source/tutorial/framework.rst +++ /dev/null @@ -1,17 +0,0 @@ -=========================================== -机器学习框架 -=========================================== - - - - -.. toctree:: - :maxdepth: 1 - :caption: 示例教程 - - - 训练和部署PyTorch模型 - 训练和部署XGBoost模型 - 训练和部署Tensorflow模型 - 基于HuggingFace BERT训练和部署文本分类模型 - 使用ModelScope ViT训练和部署图片分类模型 diff --git a/docs/source/tutorial/huggingface_bert/huggingface_bert.ipynb b/docs/source/tutorial/huggingface_bert/huggingface_bert.ipynb deleted file mode 100644 index cb90726..0000000 --- a/docs/source/tutorial/huggingface_bert/huggingface_bert.ipynb +++ /dev/null @@ -1,848 +0,0 @@ -{ - "cells": [ - { - "attachments": {}, - "cell_type": "markdown", - "id": "bb57c39e-16f6-4f84-b071-7751bd01b4c4", - "metadata": { - "ExecutionIndicator": { - "show": true - }, - "tags": [] - }, - "source": [ - "# HuggingFace BERT模型部署和微调训练\n", - "\n", - "[HuggingFace](https://huggingface.co/) 是一个开源开放的AI社区平台,允许用户共享自己的AI项目、数据集和模型,同时也为用户提供了各种机器学习工具,包括`transformers`、`diffusers`、`accelerate`等。通过HuggingFace社区,用户可以轻松地构建和训练自己的模型,并将其应用于各种实际场景中。\n", - "\n", - "当前文档中,我们以HuggingFace提供的[BERT预训练模型-英文-base](https://huggingface.co/bert-base-uncased)预训练模型为示例,展示如何在PAI微调训练和部署BERT模型,主要内容包括以下:\n", - "\n", - "1. SDK安装和配置:\n", - "\n", - "安装所需的SDK,并完成PAI Python SDK配置。\n", - "\n", - "2. 直接部署BERT模型创建推理服务\n", - "\n", - "将HuggingFace上的BERT模型直接模型部署到PAI-EAS,创建一个在线推理服务。\n", - "\n", - "3. 使用BERT模型微调训练\n", - "\n", - "基于BERT模型,我们使用公共数据集进行微调训练,以获得一个可以用于情感分类的模型,然后将输出的模型部署到PAI-EAS,创建一个在线推理服务。\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "73692608-6d3f-4551-9eeb-e169bfa93799", - "metadata": {}, - "source": [ - "## Step1: SDK的安装配置\n", - "\n", - "我们将使用PAI提供的Python SDK,提交训练作业,部署模型。请通过以下命令安装PAI Python SDK,以及需要使用到的Huggingface datasets等依赖库。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c09a58a3-7cf9-43ac-b386-3bafffbf6321", - "metadata": { - "ExecutionIndicator": { - "show": true - }, - "tags": [ - "skip-execution" - ] - }, - "outputs": [], - "source": [ - "!python -m pip install --upgrade alipai" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3bee87d5", - "metadata": {}, - "outputs": [], - "source": [ - "\n", - "!python -m pip install datasets huggingface_hub" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "5212ae4f-cb05-45a0-82f1-3d1cd89be38b", - "metadata": {}, - "source": [ - "\n", - "SDK需要配置访问阿里云服务需要的AccessKey,以及当前使用的工作空间和OSS Bucket。在PAI Python SDK安装之后,通过在**命令行终端**中执行以下命令,按照引导配置密钥,工作空间等信息。\n", - "\n", - "\n", - "```shell\n", - "\n", - "# 以下命令,请在命令行终端中执行.\n", - "\n", - "python -m pai.toolkit.config\n", - "\n", - "```\n", - "\n", - "我们可以通过执行以下代码验证当前的配置是否成功。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "55bcb9aa-58ee-47a0-9656-446c5bf67845", - "metadata": { - "ExecutionIndicator": { - "show": true - }, - "tags": [] - }, - "outputs": [], - "source": [ - "import pai\n", - "from pai.session import get_default_session\n", - "\n", - "print(pai.__version__)\n", - "sess = get_default_session()\n", - "\n", - "assert sess.workspace_name is not None" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "5952d3b9", - "metadata": {}, - "source": [ - "## Step2: 部署BERT模型创建推理服务\n", - "\n", - "\n", - "[PAI-EAS](https://www.aliyun.com/activity/bigdata/pai/eas) (Elastic Algorithm Service) 是PAI平台上的模型在线预测服务,支持使用镜像模式部署模型,并且提供了常见的机器学习框架的推理镜像。 在以下示例中,我们将使用PAI-EAS提供的镜像,将HuggingFace上的BERT模型直接部署到PAI,创建一个在线推理服务。\n", - "\n", - "[BERT](https://arxiv.org/abs/1810.04805)是Google提出的一种预训练语言模型,使用自监督学习方法在大型英文语料库上进行训练。他可以直接用于\"完形填空\"的任务,也可以作为下游任务的预训练模型,通过微调训练,用于分类,问答等不同的任务。我们通过以下代码下载HuggingFace提供的BERT模型,用于创建一个支持“完形填空”的推理服务。\n", - "\n", - "> 对于如何在离线模式下保存和使用HuggingFace模型,用户可以参考HuggingFace的官方文档: [HuggingFace Offline Mode](https://huggingface.co/docs/transformers/installation#fetch-models-and-tokenizers-to-use-offline)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "25ae41ce", - "metadata": {}, - "outputs": [], - "source": [ - "from huggingface_hub import snapshot_download\n", - "\n", - "\n", - "# 下载BERT模型(PyTorch版本)\n", - "model_dir = snapshot_download(\n", - " repo_id=\"bert-base-uncased\",\n", - " local_dir=\"./bert\",\n", - " allow_patterns=[\n", - " \"config.json\",\n", - " \"pytorch_model.bin\",\n", - " \"vocab.txt\",\n", - " \"tokenizer_config.json\",\n", - " \"tokenizer.json\",\n", - " ],\n", - ")" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "bf489d6a", - "metadata": {}, - "source": [ - "用户也可以通过以下的方式保存模型(需要用户在本地install`transformers`, `pytorch`等依赖库):\n", - "\n", - "```python\n", - "\n", - "from transformers import BertTokenizer, BertModel\n", - "\n", - "# 下载模型\n", - "tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')\n", - "model = BertModel.from_pretrained(\"bert-base-uncased\")\n", - "\n", - "# 保存模型到本地路径\n", - "model_dir = \"./bert/\"\n", - "model.save_pretrained(model_dir)\n", - "tokenizer.save_pretrained(model_dir)\n", - "\n", - "```\n", - "\n", - "保存的模型,可以直接通过`transformers`库加载使用:\n", - "\n", - "```python\n", - "\n", - "from transformers import BertTokenizer, BertModel\n", - "\n", - "model = BertModel.from_pretrained(\"./bert/\")\n", - "tokenizer = BertTokenizer.from_pretrained(\"./bert/\")\n", - "\n", - "```\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "14acee39", - "metadata": {}, - "source": [ - "将保存在本地的BERT模型和tokenizer上传到OSS Bucket,拿到模型的OSS路径。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "25debdca", - "metadata": {}, - "outputs": [], - "source": [ - "from pai.common.oss_utils import upload\n", - "\n", - "# 上传模型\n", - "bert_model_uri = upload(\n", - " source_path=model_dir, oss_path=\"huggingface/model/bert/\", bucket=sess.oss_bucket\n", - ")\n", - "print(bert_model_uri)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "088cf89b", - "metadata": {}, - "source": [ - "\n", - "在部署模型之前,我们需要准备模型推理服务的代码,用于加载模型,提供HTTP服务。在以下示例中,我们使用[FastAPI](https://fastapi.tiangolo.com/)编写了一个简单的HTTP服务,用于加载模型,提供预测服务。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a524a388", - "metadata": {}, - "outputs": [], - "source": [ - "# 创建推理服务使用的代码\n", - "!mkdir -p serving_src" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "b804c39c", - "metadata": {}, - "source": [ - "完整的推理服务程序代码如下:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d8cd70ff", - "metadata": {}, - "outputs": [], - "source": [ - "%%writefile serving_src/run.py\n", - "\n", - "import os\n", - "import logging\n", - "\n", - "import uvicorn, json, datetime\n", - "from fastapi import FastAPI, Request\n", - "from transformers import pipeline, AutoTokenizer, AutoModelForSequenceClassification\n", - "\n", - "# 用户指定模型,默认会被加载到当前路径下\n", - "MODEL_PATH = \"/eas/workspace/model/\"\n", - "\n", - "logging.basicConfig(level=logging.INFO)\n", - "logger = logging.getLogger(\"model_server\")\n", - "\n", - "app = FastAPI()\n", - "\n", - "@app.post(\"/\")\n", - "async def predict(request: Request):\n", - " global bert_pipeline\n", - " json_data = await request.json()\n", - " logger.info(\"Input data: %s\", json_data)\n", - " result = bert_pipeline(json_data[\"text\"])\n", - " logger.info(\"Prediction result: %s\", result)\n", - " return result\n", - "\n", - "\n", - "if __name__ == '__main__':\n", - " task = os.environ.get(\"HF_TASK\", \"fill-mask\")\n", - " bert_pipeline = pipeline(task=task, model=MODEL_PATH, tokenizer=MODEL_PATH)\n", - "\n", - " uvicorn.run(app, host='0.0.0.0', port=int(os.environ.get(\"LISTENING_PORT\", 8000)))" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "77a3568b", - "metadata": {}, - "source": [ - "SDK 提供的 `pai.model.InferenceSpec` 用于描述如何加载模型,以及如何提供预测服务。在以下代码中,我们使用 `pai.model.container_serving_spec` 方法,使用 PAI 提供的推理镜像和本地代码 `serving_src`,创建一个 `InferenceSpec` 对象。对应的本地代码会被上传保存到用户OSS,然后通过挂载的方式将相应的代码准备到运行容器中。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ab6c3828", - "metadata": {}, - "outputs": [], - "source": [ - "from pai.model import Model, container_serving_spec\n", - "from pai.image import retrieve, ImageScope\n", - "\n", - "\n", - "# 使用 PAI 提供的 PyTorch CPU 推理镜像\n", - "image_uri = retrieve(\n", - " \"PyTorch\",\n", - " framework_version=\"latest\",\n", - " accelerator_type=\"CPU\",\n", - " image_scope=ImageScope.INFERENCE,\n", - ").image_uri\n", - "print(image_uri)\n", - "\n", - "\n", - "# 构建一个使用镜像部署的InferenceSpec,可以用于BERT模型部署为推理服务.\n", - "bert_inference_spec = container_serving_spec(\n", - " # 模型服务的启动命令\n", - " command=\"python run.py\",\n", - " # 模型服务依赖的代码\n", - " source_dir=\"./serving_src\",\n", - " image_uri=image_uri,\n", - " requirements=[\n", - " \"transformers\",\n", - " \"fastapi\",\n", - " \"uvicorn\",\n", - " # 推理 pipeline 使用 device_map=\"auto\" 时需要安装\n", - " \"accelerate\",\n", - " ],\n", - ")\n", - "\n", - "print(bert_inference_spec.to_dict())" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "debba5d4", - "metadata": {}, - "source": [ - "### 模型部署\n", - "\n", - "通过构建Model,调用`Model.deploy`方法,可以将模型部署到PAI-EAS,生成在线服务。\n", - "\n", - "关于如何使用SDK部署模型的详细介绍,用户可以参考文档:[PAI Python SDK部署推理服务](https://help.aliyun.com/document_detail/2261532.html)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "60b07fb7", - "metadata": {}, - "outputs": [], - "source": [ - "from pai.model import Model\n", - "from pai.common.utils import random_str\n", - "\n", - "m = Model(\n", - " inference_spec=bert_inference_spec,\n", - " model_data=bert_model_uri,\n", - ")\n", - "\n", - "p = m.deploy(\n", - " service_name=\"hf_bert_serving_{}\".format(random_str(6)), # 推理服务名称.\n", - " instance_type=\"ecs.c6.xlarge\", # 服务使用的机器实例规格: 4 vCPU, 8 GB\n", - ")" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "5e64254b", - "metadata": {}, - "source": [ - "deploy方法返回的Predictor对象,指向了新创建的推理服务,他提供了`.predict`方法,支持用户向推理服务发送预测请求。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2df66b1f", - "metadata": {}, - "outputs": [], - "source": [ - "res = p.predict(data={\"text\": \"Hello, I'm a [MASK] model.\"})\n", - "\n", - "print(res)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "57f86644", - "metadata": {}, - "source": [ - "在测试完成之后,我们可以通过`predictor.delete_service`删除推理服务,释放资源。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d78a2587", - "metadata": {}, - "outputs": [], - "source": [ - "# 执行完成之后,删除对应的服务\n", - "\n", - "p.delete_service()" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "66b601b9-030e-49c1-8534-b6a53ea7903d", - "metadata": {}, - "source": [ - "## Step3: Finetune BERT预训练模型\n", - "\n", - "[BERT](https://arxiv.org/abs/1810.04805)使用自监督学习方法在大型英文语料库上进行训练,他学习到了英语语言的内在表示,可以通过微调的方式,应用于不同的下游任务,从而获得更好的性能。在当前示例中,我们将使用Huggingface上 Yelp英文评论数据集[yelp_review_full](https://huggingface.co/datasets/yelp_review_full) 对BERT模型进行微调,以获得一个可以用于情感分类的模型。\n", - "\n", - "\n", - "### 准备模型和数据集\n", - "\n", - "在当前步骤中,我们将准备微调训练使用的数据集,然后上传到OSS上供训练作业使用。\n", - "\n", - "> 通过HuggingFace提供的transformers和datasets库可以使用读取本地文件的方式(离线模式),或是从HuggingFace Hub下载模型和数据的方式。为了提高训练作业的执行速度,我们在当前示例中,将模型和数据集准备到OSS,挂载到训练作业执行环境中,供训练作业直接加载使用。\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f760ddcb", - "metadata": {}, - "outputs": [], - "source": [ - "from datasets import load_dataset\n", - "from pai.common.oss_utils import upload\n", - "\n", - "data_path = \"./train_data\"\n", - "\n", - "# 从HuggingFace Hub加载数据集\n", - "dataset = load_dataset(\"yelp_review_full\")\n", - "\n", - "# 保存到数据集,保存的数据集可以通过`datasets.load_from_disk`加载使用\n", - "dataset.save_to_disk(data_path)\n", - "\n", - "train_data_uri = upload(\n", - " source_path=data_path,\n", - " oss_path=\"huggingface/dataset/yelp_review_full/\",\n", - " bucket=sess.oss_bucket,\n", - ")\n", - "\n", - "print(train_data_uri)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "5e518592", - "metadata": {}, - "source": [ - "\n", - "### 准备训练代码\n", - "参考HuggingFace提供的对于[Masked Language Model 的微调文档](https://huggingface.co/course/chapter7/3?fw=tf),我们编写了以下训练脚本,它将使用我们上传的数据集完成模型的微调。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a2534222", - "metadata": {}, - "outputs": [], - "source": [ - "\n", - "# 创建代码保存目录\n", - "!mkdir -p train_src" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "0f2a7761", - "metadata": {}, - "source": [ - "\n", - "在我们编写的训练作业脚本中,通过环境变量的方式获取训练作业的超参,输出数据,输出模型保存地址。对于PAI训练服务提供的环境变量的详细介绍,可以见文档:[训练作业预置环境变量](https://help.aliyun.com/document_detail/2261505.html)\n", - "\n", - "完整的训练代码如下:\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "26c62bb8-963e-4ffe-8843-715482896cd3", - "metadata": { - "ExecutionIndicator": { - "show": true - }, - "tags": [] - }, - "outputs": [], - "source": [ - "%%writefile train_src/finetune.py\n", - "\n", - "import os\n", - "\n", - "from datasets import load_dataset, load_from_disk\n", - "from transformers import AutoTokenizer, AutoModelForSequenceClassification, TrainingArguments, Trainer, DataCollatorWithPadding, HfArgumentParser\n", - "import numpy as np\n", - "import evaluate\n", - "\n", - "\n", - "def compute_metrics(eval_pred):\n", - " logits, labels = eval_pred\n", - " predictions = np.argmax(logits, axis=-1)\n", - " return metric.compute(predictions=predictions, references=labels)\n", - "\n", - "def tokenize_function(examples):\n", - " return tokenizer(examples[\"text\"], padding=\"max_length\", truncation=True)\n", - "\n", - "\n", - "def train():\n", - " # 通过环境变量获取预训练模型地址, 训练数据,以及模型保存地址\n", - " model_name_or_path = os.environ.get(\"PAI_INPUT_MODEL\", \"bert-base-cased\")\n", - " input_train_data = os.environ.get(\"PAI_INPUT_TRAIN_DATA\")\n", - " output_dir=os.environ.get(\"PAI_OUTPUT_MODEL\", \"./output\")\n", - "\n", - " # 使用环境变量获取训练作业超参\n", - " num_train_epochs=int(os.environ.get(\"PAI_HPS_EPOCHS\", 2))\n", - " save_strategy=os.environ.get(\"PAI_HPS_SAVE_STRATEGY\", \"epoch\")\n", - "\n", - " print(\"Loading Model...\")\n", - " model = AutoModelForSequenceClassification.from_pretrained(model_name_or_path, num_labels=5)\n", - " tokenizer = AutoTokenizer.from_pretrained(model_name_or_path)\n", - "\n", - " print(\"Loading dataset from disk...\")\n", - " dataset = load_from_disk(input_train_data)\n", - " tokenized_datasets = dataset.map(lambda examples: tokenizer(examples[\"text\"], padding=\"max_length\", truncation=True, max_length=512),\n", - " batched=True)\n", - "\n", - " data_collator = DataCollatorWithPadding(tokenizer)\n", - " small_train_dataset = tokenized_datasets['train'].shuffle(seed=42).select(range(1000))\n", - " small_eval_dataset = tokenized_datasets['test'].shuffle(seed=42).select(range(1000))\n", - "\n", - " training_args = TrainingArguments(\n", - " output_dir=output_dir,\n", - " # 使用环境变量获取训练作业超参\n", - " num_train_epochs=num_train_epochs,\n", - " # 使用环境变量获取训练作业保存策略\n", - " save_strategy=save_strategy,\n", - " )\n", - " print(\"TrainingArguments: {}\".format(training_args.to_json_string()))\n", - " metric = evaluate.load('accuracy')\n", - "\n", - " print(\"Training...\")\n", - " trainer = Trainer(\n", - " model=model,\n", - " args=training_args,\n", - " train_dataset=small_train_dataset,\n", - " eval_dataset=small_eval_dataset,\n", - " data_collator=data_collator,\n", - " tokenizer=tokenizer,\n", - " compute_metrics=compute_metrics,\n", - " )\n", - "\n", - " trainer.train()\n", - " print(\"Saving Model...\")\n", - " trainer.save_model()\n", - "\n", - "\n", - "if __name__ == \"__main__\":\n", - " train()\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "6a603b63", - "metadata": {}, - "source": [ - "我们的训练作业将使用PAI提供的PyTorch镜像执行,需要在镜像中安装 `transformers` 和 `evaluate` 库才能够执行相应的训练脚本。通过在训练作业目录下提供 `requirements.txt` 文件,PAI的训练服务会自动安装指定的第三方依赖。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cfab739e", - "metadata": {}, - "outputs": [], - "source": [ - "\n", - "%%writefile train_src/requirements.txt\n", - "\n", - "transformers\n", - "datasets\n", - "evaluate\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "b478975d-17bd-4f81-93b8-e3dd32b6b7f1", - "metadata": {}, - "source": [ - "### 提交训练作业\n", - "\n", - "通过PAI Python SDK提供的训练作业API`pai.estimator.Estimator`,我们可以将训练脚本提交到PAI执行。在以下代码中,我们将指定使用的训练代码 `train_src` ,使用PAI提供的PyTorch GPU镜像训练,提交运行微调训练作业。对于使用SDK提交训练作业的详细介绍,用户可以参考文档:[PAI Python SDK提交训练作业](https://help.aliyun.com/document_detail/2261505.html)。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "dda481a0-7c85-4b49-b3c5-cc30ca5d3a8c", - "metadata": { - "ExecutionIndicator": { - "show": false - }, - "tags": [] - }, - "outputs": [], - "source": [ - "from pai.huggingface.estimator import HuggingFaceEstimator\n", - "from pai.image import retrieve\n", - "\n", - "\n", - "# 使用 PAI 提供的 PyTorch GPU 训练镜像\n", - "image_uri = retrieve(\n", - " \"PyTorch\", framework_version=\"latest\", accelerator_type=\"GPU\"\n", - ").image_uri\n", - "\n", - "\n", - "# 配置训练作业\n", - "est = HuggingFaceEstimator(\n", - " command=\"python finetune.py\", # 训练作业启动命令\n", - " source_dir=\"./train_src/\", # 训练作业代码\n", - " instance_type=\"ecs.gn6i-c4g1.xlarge\", # 训练使用的作业机器类型, 4 vCPU, 15 GB, 1* T4 GPU\n", - " transformers_version=\"latest\",\n", - " hyperparameters={ # 训练作业超参,用户可以通过环境变量,或是\n", - " \"save_strategy\": \"epoch\",\n", - " \"epochs\": \"1\",\n", - " },\n", - " base_job_name=\"hf-bert-training\",\n", - ")\n", - "\n", - "\n", - "# est = Estimator(\n", - "# image_uri=image_uri, # 训练作业使用的镜像\n", - "# command=\"python finetune.py\", # 训练作业启动命令\n", - "# source_dir=\"./train_src/\", # 训练作业代码\n", - "# instance_type=\"ecs.gn6i-c4g1.xlarge\", # 训练使用的作业机器类型, 4 vCPU, 15 GB, 1* T4 GPU\n", - "# hyperparameters={ # 训练作业超参,用户可以通过环境变量,或是\n", - "# \"save_strategy\": \"epoch\",\n", - "# \"epochs\": \"1\",\n", - "# },\n", - "# base_job_name=\"hf-bert-training\",\n", - "# )\n", - "\n", - "print(est)\n", - "print(est.hyperparameters)\n", - "\n", - "# 提交训练作业到PAI执行\n", - "# 提交之后SDK会打印作业URL,我们可以作业详情页查看训练日志,输出模型,资源使用情况等\n", - "est.fit(\n", - " # 作业使用的预训练模型和数据集使用inputs方式传递\n", - " # 相应的OSS URI会被挂载到作业环境中,用户可以通过 `PAI_INPUT_{ChannelNameUpperCase}` 的环境变量获取挂载后的路径\n", - " inputs={\n", - " \"model\": bert_model_uri,\n", - " \"train_data\": train_data_uri,\n", - " }\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3c0354a7", - "metadata": {}, - "outputs": [], - "source": [ - "# 训练任务产出的模型地址\n", - "print(est.model_data())" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "27e83a0d-d02c-42f3-bbd2-c71c775fad82", - "metadata": { - "tags": [] - }, - "source": [ - "### 部署Finetune获得的模型\n", - "\n", - "我们将复用以上推理服务的代码,将微调训练获得的模型部署到PAI-EAS,创建一个在线推理服务。\n", - "\n", - "> Note: 微调模型用于情感分析任务,我们显式得修改HuggingFace pipeline的Task参数。这里我们通过环境变量的方式传入Task参数。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "73a51721-11b9-4f24-b016-5c704de526b8", - "metadata": { - "ExecutionIndicator": { - "show": false - }, - "tags": [] - }, - "outputs": [], - "source": [ - "from pai.model import Model, container_serving_spec\n", - "from pai.image import retrieve, ImageScope\n", - "\n", - "\n", - "# 使用 PAI 提供的 PyTorch CPU 推理镜像\n", - "image_uri = retrieve(\n", - " \"PyTorch\",\n", - " framework_version=\"latest\",\n", - " accelerator_type=\"CPU\",\n", - " image_scope=ImageScope.INFERENCE,\n", - ").image_uri\n", - "\n", - "\n", - "# 构建一个使用镜像部署的InferenceSpec,可以用于将以上产出的BERT模型部署为推理服务.\n", - "inference_spec = container_serving_spec(\n", - " # 模型服务的启动命令\n", - " command=\"python run.py\",\n", - " # 模型服务依赖的代码\n", - " source_dir=\"./serving_src\",\n", - " image_uri=image_uri,\n", - " requirements=[\n", - " \"transformers\",\n", - " \"fastapi\",\n", - " \"uvicorn\",\n", - " ],\n", - " # 使用情感分析任务pipeline,通过环境变量的方式传递给到推理服务脚本。\n", - " environment_variables={\"HF_TASK\": \"sentiment-analysis\"},\n", - ")\n", - "\n", - "print(inference_spec.to_dict())" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d57a41f3-a4fc-40d8-92d5-ca083f4de2ee", - "metadata": { - "ExecutionIndicator": { - "show": false - }, - "tags": [] - }, - "outputs": [], - "source": [ - "from pai.model import Model\n", - "from pai.common.utils import random_str\n", - "\n", - "# 使用训练作业产出的模型\n", - "model_data = est.model_data()\n", - "\n", - "m = Model(\n", - " inference_spec=inference_spec,\n", - " model_data=model_data,\n", - ")\n", - "\n", - "p = m.deploy(\n", - " service_name=\"hf_bert_ft_serving_{}\".format(random_str(6)), # 推理服务名称\n", - " instance_type=\"ecs.c6.xlarge\", # 服务使用的机器实例规格: 4 vCPU, 8 GB\n", - ")" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "82e7586d", - "metadata": {}, - "source": [ - "通过Predictor向新创建的推理服务发送预测请求,获取模型预测结果。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "af0b6e0b", - "metadata": {}, - "outputs": [], - "source": [ - "res = p.predict({\"text\": \"i am so happy today\"})\n", - "print(res)\n", - "\n", - "res = p.predict({\"text\": \"i am so sad today\"})\n", - "print(res)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "bc2bdce3", - "metadata": {}, - "source": [ - "在测试完成之后,我们通过`predictor.delete_service`删除推理服务,释放资源。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4fdd724e-e557-4461-9cdb-93874a77c49a", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "# 执行完成之后,删除对应的服务\n", - "\n", - "p.delete_service()" - ] - } - ], - "metadata": { - "execution": { - "timeout": 1800 - }, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.3" - }, - "vscode": { - "interpreter": { - "hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6" - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/docs/source/tutorial/huggingface_model_deploy/huggingface_model_deploy.ipynb b/docs/source/tutorial/huggingface_model_deploy/huggingface_model_deploy.ipynb deleted file mode 100644 index c04412c..0000000 --- a/docs/source/tutorial/huggingface_model_deploy/huggingface_model_deploy.ipynb +++ /dev/null @@ -1,181 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# 部署HuggingFace模型\n", - "\n", - "HuggingFace是一个开源的模型社区,机器学习开发者在社区中可以分享、发现和使用各类机器学习模型。\n", - "\n", - "本文将介绍如何将HuggingFace社区的模型部署到PAI创建模型推理服务。" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "## 安装和配置SDK\n", - "\n", - "\n", - "我们需要首先安装PAI Python SDK以运行本示例。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "\n", - "!python -m pip install --upgrade alipai" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "\n", - "SDK需要配置访问阿里云服务需要的AccessKey,以及当前使用的工作空间和OSS Bucket。在PAI SDK安装之后,通过在**命令行终端** 中执行以下命令,按照引导配置密钥、工作空间等信息。\n", - "\n", - "\n", - "```shell\n", - "\n", - "# 以下命令,请在 命令行终端 中执行.\n", - "\n", - "python -m pai.toolkit.config\n", - "\n", - "```\n", - "\n", - "我们可以通过以下代码验证配置是否已生效。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import pai\n", - "from pai.session import get_default_session\n", - "\n", - "print(pai.__version__)\n", - "\n", - "sess = get_default_session()\n", - "\n", - "# 获取配置的工作空间信息\n", - "assert sess.workspace_name is not None\n", - "print(sess.workspace_name)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 部署HuggingFace模型\n", - "\n", - "在本示例中,我们将使用HuggingFace社区提供的情感分类模型 [distilbert-base-uncased-finetuned-sst-2-english](https://huggingface.co/distilbert-base-uncased-finetuned-sst-2-english)部署一个模型在线服务,他支持将一段英文文本分类为正面或负面情感。\n", - "\n", - "通过相应的[模型的详情页](https://huggingface.co/distilbert-base-uncased-finetuned-sst-2-english/tree/main),我们可以获取部署模型所需的信息,包括模型ID(``MODEL_ID``)、模型任务类型(``TASK``)、模型版本(``REVISION``)。\n", - "\n", - "![](../../images/huggingface-model.png)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "通过PAI Python SDK提供的``HuggingFaceModel``,我们可以轻松地将HuggingFace社区的模型部署到PAI上。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pai.huggingface import HuggingFaceModel\n", - "\n", - "\n", - "# 初始化一个HuggingFaceModel\n", - "m = HuggingFaceModel(\n", - " command=\"python app.py\", # 模型服务启动命令\n", - " transformers_version=\"latest\", # 使用的transformers版本, 'latest'表示使用PAI目前支持的最新的版本\n", - " environment_variables={\n", - " \"MODEL_ID\": \"distilbert-base-uncased-finetuned-sst-2-english\", # 部署模型的ID\n", - " \"TASK\": \"text-classification\", # 部署的模型任务类型\n", - " \"REVISION\": \"main\", # 部署模型的版本信息\n", - " },\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pai.common.utils import random_str\n", - "\n", - "\n", - "# 部署模型,创建一个模型在线服务\n", - "p = m.deploy(\n", - " service_name=f\"hf_model_deploy_{random_str(n=8)}\", # 模型服务的名称(地域内唯一)\n", - " instance_type=\"ecs.g6.large\", # 模型服务使用的机器实例规格\n", - " options={\n", - " \"enable_webservice\": True, # 以AIWeb应用的模式启动,支持用户在Web浏览器上使用模型在线服务\n", - " },\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "p.predict(data={\"data\": [\"I love you\"]})" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "测试完成之后,删除服务,释放机器资源。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "p.delete_service()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "base", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.3" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/source/tutorial/model_deploy_container/model_deploy_container.ipynb b/docs/source/tutorial/model_deploy_container/model_deploy_container.ipynb deleted file mode 100644 index 229bc2b..0000000 --- a/docs/source/tutorial/model_deploy_container/model_deploy_container.ipynb +++ /dev/null @@ -1,376 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# 使用镜像部署模型\n", - "\n", - "PAI支持用户使用镜像的方式部署模型,通过镜像,开发者可以自定义模型部署的环境,包括Python、使用的机器学习框架、依赖的第三方库等,能够支持用户灵活的部署需求。详细的介绍可以参考PAI帮助文档:[使用镜像部署模型](https://help.aliyun.com/zh/pai/user-guide/deploy-a-model-service-by-using-a-custom-image)。\n", - "\n", - "PAI Python SDK提供了便利的API,支持用户能够使用自定义镜像,或是PAI提供的预置推理,将一个本地,或是OSS上的模型快捷得部署为模型在线服务。\n", - "\n", - "本文档将介绍,用户如何通过PAI Python SDK通过自定义镜像的方式部署模型。" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "## 安装和配置SDK\n", - "\n", - "我们需要首先安装PAI Python SDK以运行本示例。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!python -m pip install --upgrade alipai" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "SDK需要配置访问阿里云服务需要的AccessKey,以及当前使用的工作空间和OSS Bucket。在PAI SDK安装之后,通过在 **命令行终端** 中执行以下命令,按照引导配置密钥、工作空间等信息。\n", - "\n", - "\n", - "```shell\n", - "\n", - "# 以下命令,请在 命令行终端 中执行.\n", - "\n", - "python -m pai.toolkit.config\n", - "\n", - "```\n", - "\n", - "我们可以通过以下代码验证配置是否已生效。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import pai\n", - "from pai.session import get_default_session\n", - "\n", - "print(pai.__version__)\n", - "\n", - "sess = get_default_session()\n", - "\n", - "# 获取配置的工作空间信息\n", - "assert sess.workspace_name is not None\n", - "print(sess.workspace_name)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 部署模型推理服务\n", - "\n", - "模型在线服务包含了模型的文件、模型的推理服务代码、以及推理服务运行环境。\n", - "本示例将使用一个简单的`PyTorch`模型,通过`Flask`和`PAI`提供的`PyTorch`基础镜像,部署模型在线服务。\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "下载示例使用的简单PyTorch模型。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# 下载模型到本地 \"model\" 目录\n", - "\n", - "!mkdir -p model/\n", - "!wget https://pai-sdk.oss-cn-shanghai.aliyuncs.com/pai/resources/toy_model.pt -P model/" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 准备推理服务代码\n", - "\n", - "在部署模型之前,我们首先需要准备推理服务的代码,它提供HTTP接口,负责接收预测请求,使用模型进行推理,返回预测结果。\n", - "\n", - "当前示例我们将使用 ``Flask`` 编写一个简单的推理服务,保存为 ``infer_src/app.py`` 文件。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!mkdir -p infer_src" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%%writefile infer_src/app.py\n", - "import json\n", - "from flask import Flask, request\n", - "import os\n", - "import torch\n", - "import numpy as np\n", - "\n", - "app = Flask(__name__)\n", - "model = None\n", - "# 默认的模型文件路径\n", - "MODEL_PATH = \"/eas/workspace/model/\"\n", - "\n", - "def load_model():\n", - " \"\"\"加载模型\"\"\"\n", - " global model\n", - " model = torch.jit.load(os.path.join(MODEL_PATH, \"toy_model.pt\"))\n", - " model.eval()\n", - "\n", - "@app.route(\"/\", methods=[\"POST\"])\n", - "def predict():\n", - " data = np.asarray(json.loads(request.data)).astype(np.float32)\n", - " output_tensor = model(torch.from_numpy(data))\n", - " pred_res = output_tensor.detach().cpu().numpy()\n", - " return json.dumps(pred_res.tolist())\n", - "\n", - "if __name__ == \"__main__\":\n", - " load_model()\n", - " app.run(host=\"0.0.0.0\", port=int(os.environ.get(\"LISTENING_PORT\", 8000)))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 获取PAI提供的预置推理镜像\n", - "\n", - "PAI提供了一系列预置的推理镜像,镜像内预置了机器学习框架、常用的第三方库、Python、NVIDIA CUDA库等。我们可以通过以下代码列出所有的预置镜像。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pai.image import list_images, ImageScope\n", - "\n", - "\n", - "data = [\n", - " [\n", - " \"ImageUri\",\n", - " \"FrameworkName\",\n", - " \"FrameworkVersion\",\n", - " \"AcceleratorType\",\n", - " \"PythonVersion\",\n", - " ]\n", - "]\n", - "\n", - "# 列出常用的PyTorch推理镜像\n", - "for img in list_images(framework_name=\"PyTorch\", image_scope=ImageScope.INFERENCE):\n", - " data.append(\n", - " [\n", - " img.image_uri,\n", - " img.framework_name,\n", - " img.framework_version,\n", - " img.accelerator_type,\n", - " img.python_version,\n", - " ]\n", - " )\n", - "\n", - "# 列出常用的TensorFlow推理镜像\n", - "for img in list_images(framework_name=\"TensorFlow\", image_scope=ImageScope.INFERENCE):\n", - " data.append(\n", - " [\n", - " img.image_uri,\n", - " img.framework_name,\n", - " img.framework_version,\n", - " img.accelerator_type,\n", - " img.python_version,\n", - " ]\n", - " )" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from IPython.display import HTML, display\n", - "\n", - "display(\n", - " HTML(\n", - " \"{}
\".format(\n", - " \"\".join(\n", - " \"{}\".format(\"\".join(str(_) for _ in row))\n", - " for row in data\n", - " )\n", - " )\n", - " )\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "通过SDK提供的 `pai.image.retrieve` API,可以获取指定框架版本的镜像。在当前示例中,我们将使用PAI提供的PyTorch 1.12版本的CPU推理镜像" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pai.image import retrieve, ImageScope\n", - "\n", - "# # 获取PyTorch 1.10 GPU推理镜像\n", - "# print(retrieve(\n", - "# framework_name=\"PyTorch\", # 框架名称\n", - "# framework_version=\"latest\", # 框架版本\n", - "# accelerator_type=\"gpu\", # 选择支持Nvidia CUDA GPU的镜像\n", - "# image_scope=ImageScope.INFERENCE, # 镜像类型,推理镜像\n", - "\n", - "# # ).image_uri)\n", - "\n", - "# 获取最新的PyTorch CPU推理镜像\n", - "torch_image_uri = retrieve(\n", - " framework_name=\"PyTorch\", # 框架名称\n", - " framework_version=\"1.12\", # 框架版本,latest表示使用PAI支持的最新版本\n", - " # accelerator_type=\"cpu\", # 默认使用CPU镜像\n", - " image_scope=ImageScope.INFERENCE, # 镜像类型,推理镜像\n", - ").image_uri\n", - "print(torch_image_uri)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 部署推理服务\n", - "使用以上的推理服务代码,以及PyTorch推理镜像,我们将一个PyTorch模型部署为模型在线服务。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pai.model import Model, container_serving_spec\n", - "\n", - "\n", - "m = Model(\n", - " model_data=\"./model/\", # 模型文件,可以是一个本地文件或是OSS Bucket路径(例如 oss:///path/to/model ),\n", - " inference_spec=container_serving_spec(\n", - " image_uri=torch_image_uri, # 推理服务使用的镜像\n", - " command=\"python app.py\", # 模型推理服务启动命令\n", - " source_dir=\"./infer_src/\", # 推理服务代码所在目录\n", - " requirements=[\"flask==2.0.0\", \"Werkzeug==2.3.4\"], # 推理服务依赖的Python包\n", - " ),\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pai.common.utils import random_str\n", - "\n", - "# 部署模型服务\n", - "p = m.deploy(\n", - " service_name=f\"toy_model_{random_str(6)}\", # 模型服务名称, 地域内唯一\n", - " instance_type=\"ecs.c6.large\", # 模型服务使用的机器实例规格\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 调用推理服务\n", - "\n", - "部署服务后返回的`pai.predictor.Predictor`对象可以用于调用推理服务,发送预测请求。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "\n", - "# 构造一个随机数组输入\n", - "dummy_input = np.random.rand(1, 10, 10).tolist()\n", - "print(dummy_input)\n", - "\n", - "result = p.raw_predict(\n", - " data=dummy_input,\n", - ")\n", - "\n", - "# 打印推理结果\n", - "print(result.json())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "在测试完成之后,删除推理服务" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "p.delete_service()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "base", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.3" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/source/tutorial/modelscope_model_deploy/modelscope_model_deploy.ipynb b/docs/source/tutorial/modelscope_model_deploy/modelscope_model_deploy.ipynb deleted file mode 100644 index 51c7c5b..0000000 --- a/docs/source/tutorial/modelscope_model_deploy/modelscope_model_deploy.ipynb +++ /dev/null @@ -1,216 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "tags": [ - "keep-output" - ] - }, - "source": [ - "# 部署ModelScope模型\n", - "\n", - "[ModelScope](https://www.modelscope.cn/)是一个开源的模型社区,提供了丰富的自然语言处理、计算机视觉、多模态等领域开源模型,并提供了[ModelScope library](https://github.com/modelscope/modelscope),支持开发者可以方便得获取模型,使用模型进行推理。\n", - "\n", - "PAI支持开发者将ModelScope上的模型,简单快捷得部署为在线推理服务,本文将介绍使用PAI Python SDK完成ModelScope模型的部署。" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 安装和配置SDK\n", - "\n", - "我们需要首先安装PAI Python SDK以运行本示例。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [ - "skip-execution" - ] - }, - "outputs": [], - "source": [ - "!python -m pip install --upgrade alipai" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "SDK需要配置访问阿里云服务需要的AccessKey,以及当前使用的工作空间和OSS Bucket。在PAI SDK安装之后,通过在 **命令行终端** 中执行以下命令,按照引导配置密钥、工作空间等信息。\n", - "\n", - "\n", - "```shell\n", - "\n", - "# 以下命令,请在 命令行终端 中执行.\n", - "\n", - "python -m pai.toolkit.config\n", - "\n", - "```\n", - "\n", - "我们可以通过以下代码验证配置是否已生效。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import pai\n", - "from pai.session import get_default_session\n", - "\n", - "print(pai.__version__)\n", - "\n", - "sess = get_default_session()\n", - "\n", - "# 获取配置的工作空间信息\n", - "assert sess.workspace_name is not None\n", - "print(sess.workspace_name)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 部署ModelScope模型\n", - "\n", - "当前示例,我们将使用ModelScope上的[\"CSANMT连续语义增强机器翻译-英中-通用领域-large\"](https://modelscope.cn/models/damo/nlp_csanmt_translation_en2zh/summary)模型,他支持英文到中文的翻译任务。\n", - "\n", - "通过ModelScope的模型详情页,我们可以获取部署模型所需要的信息,包括**模型ID**,**模型版本**,以及**任务类型**,然后通过 `pai.modelscope.ModelScopeModel` 类,创建一个ModelScope模型对象,完成模型部署。\n", - "\n", - "![](../../images/modelscope-model.png)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pai.modelscope.model import ModelScopeModel\n", - "\n", - "# 配置待部署的模型信息\n", - "m = ModelScopeModel(\n", - " command=\"python app.py\", # 默认的ModelScope模型推理服务启动命令\n", - " modelscope_version=\"latest\", # ModelScope library的版本号,latest表示最新版本\n", - " environment_variables={\n", - " \"MODEL_ID\": \"damo/nlp_csanmt_translation_en2zh\", # ModelScope的模型ID\n", - " \"TASK\": \"translation\", # 模型的任务类型\n", - " \"REVISION\": \"v1.0.1\", # 模型的版本号\n", - " },\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pai.common.utils import random_str\n", - "from pai.predictor import Predictor\n", - "\n", - "# 部署模型,在PAI-EAS创建一个推理服务\n", - "p: Predictor = m.deploy(\n", - " service_name=\"ms_model_{0}\".format(random_str(8)), # 配置推理服务名称\n", - " instance_type=\"ecs.gn6i-c4g1.xlarge\", # 配置推理服务实例规格\n", - " options={\n", - " \"metadata.rpc.keepalive\": 20000, # 配置推理服务RPC超时时间: 20s\n", - " },\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "通过以上方式部署的模型推理服务,支持通过空字符串的预测请求,获取模型的输入输出信息。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pprint import pprint\n", - "from pai.predictor import RawResponse\n", - "\n", - "# 通过一个空的预测请求,获取模型的推理输入输出的数据格式\n", - "res: RawResponse = p.raw_predict(data=\"\")\n", - "\n", - "pprint(res.json())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "基于以上获得的输入数据格式信息,我们可以构建相应的预测请求,发送给到推理,获取翻译结果。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "res = p.predict(\n", - " # 参考以上的获得的输入输出数据格式,配置推理请求的数据\n", - " data={\n", - " \"input\": {\n", - " \"text\": \"Alibaba Group's mission is to let the world have no difficult business\"\n", - " }\n", - " }\n", - ")\n", - "pprint(res)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "在测试完成之后,删除推理服务,释放机器资源。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# 删除推理服务\n", - "p.delete_service()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "base", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.3" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/source/tutorial/modelscope_vit/modelscope_vit.ipynb b/docs/source/tutorial/modelscope_vit/modelscope_vit.ipynb deleted file mode 100644 index 106f994..0000000 --- a/docs/source/tutorial/modelscope_vit/modelscope_vit.ipynb +++ /dev/null @@ -1,638 +0,0 @@ -{ - "cells": [ - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# 使用ModelScope ViT模型完成图像分类模型微调和部署\n", - "\n", - "## 背景介绍\n", - "\n", - "\n", - "[ModelScope](https://www.modelscope.cn)是一个旨在为泛AI开发者提供灵活、易用、低成本的一站式“模型即服务”(MaaS)的开源平台。它汇集了丰富的预训练模型,覆盖了NLP、CV、Audio、AIGC、多模态大模型等多个领域。利用ModelScope所提供的模型以及ModelScope Library,开发者可以用一行代码实现模型推理,或者用十几行代码实现对预训练模型的调优训练,方便开发者基于行业数据集快速构建专属行业模型。\n", - "\n", - "当前示例中,我们以[ViT图像分类-通用](https://modelscope.cn/models/damo/cv_vit-base_image-classification_ImageNet-labels/summary) 为示例,展示如何在PAI完成一个ModelScope模型的微调训练,然后将获得的模型部署为一个在线推理服务的过程。主要流程包括:\n", - "\n", - "1. 准备工作:\n", - "\n", - "安装PAI Python SDK,并完成SDK配置。\n", - "\n", - "2. 模型的微调训练\n", - "\n", - "编写微调训练脚本,使用[花朵分类](https://www.modelscope.cn/models/zydfx1111/flower)数据集对模型进行微调训练,以获得一个可以用于花朵分类的模型。\n", - "\n", - "3. 部署推理服务\n", - "\n", - "将微调训练作业输出的模型,部署到PAI-EAS,创建一个在线推理服务。\n", - "\n", - "## 前提条件\n", - "\n", - "- 已获取阿里云账号的鉴权AccessKey ID和AccessKey Secret,详情请参见:[获取AccessKey](https://help.aliyun.com/document_detail/116401.html)。\n", - "- 已创建或是加入一个PAI AI工作空间,详情请参见:[创建工作空间](https://help.aliyun.com/document_detail/326193.html)。\n", - "- 已创建OSS Bucket,详情请参见:[控制台创建存储空间](https://help.aliyun.com/document_detail/31885.html)。\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Step1: 准备工作\n", - "\n", - "我们将使用PAI提供的Python SDK,提交训练作业,部署模型。可以通过以下命令安装PAI Python SDK。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [ - "skip-execution" - ] - }, - "outputs": [], - "source": [ - "!python -m pip install --upgrade alipai" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "SDK需要配置访问阿里云服务需要的 AccessKey,以及当前使用的工作空间和OSS Bucket。在PAI Python SDK安装之后,通过在 **命令行终端** 中执行以下命令,按照引导配置密钥,工作空间等信息。\n", - "\n", - "\n", - "```shell\n", - "\n", - "# 以下命令,请在 命令行终端 中执行.\n", - "\n", - "python -m pai.toolkit.config\n", - "\n", - "```\n", - "\n", - "我们可以通过以下代码验证当前的配置。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import pai\n", - "from pai.session import get_default_session\n", - "\n", - "print(pai.__version__)\n", - "sess = get_default_session()\n", - "\n", - "assert sess.workspace_name is not None\n", - "print(sess.workspace_name)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Step2: 提交微调训练作业\n", - "\n", - "ModelScope的[ViT图片分类-通用](https://modelscope.cn/models/damo/cv_vit-base_image-classification_ImageNet-labels/summary)模型使用经典的[ViT Base](https://github.com/google-research/vision_transformer)模型结构,在ImageNet-1k数据集进行预训练,可以直接用于[ImageNet 1k标签](https://deeplearning.cms.waikato.ac.nz/user-guide/class-maps/IMAGENET/)覆盖图像的分类任务,也可以作为下游任务的预训练模型。\n", - "\n", - "当前示例,我们将以[花朵分类数据集](https://www.modelscope.cn/datasets/tany0699/flowers14/summary)对模型进行微调训练,从而获得一个可以用于花朵分类的模型。\n", - "\n", - "### 准备微调训练脚本\n", - "\n", - "ModelScope提供了功能完善的Python Library,能够支持用户方便得使用ModelScope模型进行推理以及微调训练,在本示例中,我们将使用ModelScope Library编写相应的微调训练脚本,然后提交到PAI执行微调训练作业。\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "\n", - "# 准备相应训练作业脚本目录\n", - "!mkdir -p train_src" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "完整的微调训练脚本代码如下:\n", - "\n", - "> 对于ModelScope library的使用介绍,请参见:[ModelScope文档](https://www.modelscope.cn/docs/ModelScope%20Library%E6%A6%82%E8%A7%88%E4%BB%8B%E7%BB%8D)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%%writefile train_src/finetune.py\n", - "\n", - "import os\n", - "import re\n", - "import logging\n", - "import shutil\n", - "\n", - "\n", - "from modelscope.msdatasets import MsDataset\n", - "from modelscope.metainfo import Trainers\n", - "from modelscope.trainers import build_trainer\n", - "\n", - "\n", - "# 从环境变量中获取超参(由PAI的训练服务注入)\n", - "BATCH_SIZE = int(os.environ.get(\"PAI_HPS_BATCH_SIZE\", 16))\n", - "LEARNING_RATE = float(os.environ.get(\"PAI_HPS_INITIAL_LEARNING_RATE\", 1e-3))\n", - "NUM_EPOCHS = int(os.environ.get(\"PAI_HPS_EPOCHS\", 1))\n", - "NUM_CLASSES = int(os.environ.get(\"PAI_HPS_NUM_CLASSES\", 14))\n", - "MODEL_ID_OR_PATH = os.environ.get(\"PAI_INPUT_MODEL\", \"damo/cv_vit-base_image-classification_ImageNet-labels\")\n", - "\n", - "# 通过环境变量获取输出模型,和checkpoints保存路径\n", - "OUTPUT_MODEL_DIR = os.environ.get(\"PAI_OUTPUT_MODEL\", \"./model/\")\n", - "WORK_DIR = os.environ.get(\"PAI_OUTPUT_CHECKPOINTS\", \"./checkpoints/\")\n", - "\n", - "\n", - "# 将产出的模型保存到模型输出目录(OUTPUT_MODEL_DIR)\n", - "def save_model():\n", - " best_ckpt_pattern = re.compile(\n", - " pattern=r\"^best_accuracy_top-1_epoch_\\d+.pth$\"\n", - " )\n", - " print(\"Saving best checkpoint as pytorch_model.pt\")\n", - " print(\"List work dir: \", os.listdir(WORK_DIR))\n", - "\n", - " f_name = next((f for f in os.listdir(WORK_DIR) if best_ckpt_pattern.match(f)), None)\n", - " if f_name:\n", - " # 使用最佳checkpoints作为输出模型\n", - " print(\"Found best checkpoint: \", f_name)\n", - " shutil.copyfile(\n", - " src=os.path.join(WORK_DIR, f_name),\n", - " dst=os.path.join(OUTPUT_MODEL_DIR, \"pytorch_model.pt\"),\n", - " )\n", - " os.remove(os.path.join(WORK_DIR, f_name))\n", - " else:\n", - " # 如果没有,则使用最后一个epoch的checkpoints作为输出模型\n", - " print(\"Not found best checkpoint.\")\n", - " last_ckpt_file = \"epoch_{}.pth\".format(NUM_EPOCHS)\n", - " if os.path.isfile(os.path.join(WORK_DIR, last_ckpt_file)):\n", - " shutil.copyfile(\n", - " src=os.path.join(WORK_DIR, last_ckpt_file),\n", - " dst=os.path.join(OUTPUT_MODEL_DIR, \"pytorch_model.pt\"),\n", - " )\n", - " else:\n", - " print(\"Not found latest checkpoint: {}.\".format(os.path.join(WORK_DIR, last_ckpt_file)))\n", - " # 模型配置信息\n", - " shutil.copyfile(\n", - " src=os.path.join(WORK_DIR, \"configuration.json\"),\n", - " dst=os.path.join(OUTPUT_MODEL_DIR, \"configuration.json\"),\n", - " )\n", - "\n", - "\n", - "# 修改配置文件\n", - "def cfg_modify_fn(cfg):\n", - " cfg.train.dataloader.batch_size_per_gpu = BATCH_SIZE # batch大小\n", - " cfg.train.dataloader.workers_per_gpu = 8 # 每个gpu的worker数目\n", - " cfg.train.max_epochs = NUM_EPOCHS # 最大训练epoch数\n", - " cfg.model.mm_model.head.num_classes = NUM_CLASSES # 分类数\n", - " cfg.model.mm_model.train_cfg.augments[0].num_classes = NUM_CLASSES # 分类数\n", - " cfg.model.mm_model.train_cfg.augments[1].num_classes = NUM_CLASSES # 分类数\n", - " cfg.train.optimizer.lr = LEARNING_RATE # 学习率\n", - " cfg.train.lr_config.warmup_iters = 1 # 预热次数\n", - "\n", - " # Note: OSS挂载到输出路径中,不支持软链接.\n", - " cfg.train.checkpoint_config.create_symlink = False\n", - "\n", - "\n", - " return cfg\n", - "\n", - "def train():\n", - " ms_train_dataset = MsDataset.load(\n", - " 'flowers14', namespace='tany0699',\n", - " subset_name='default', split='train') # 加载训练集\n", - "\n", - " ms_val_dataset = MsDataset.load(\n", - " 'flowers14', namespace='tany0699',\n", - " subset_name='default', split='validation') # 加载验证集\n", - "\n", - "\n", - " # 构建训练器\n", - " kwargs = dict(\n", - " model=MODEL_ID_OR_PATH, # 模型id\n", - " work_dir=WORK_DIR,\n", - " train_dataset=ms_train_dataset, # 训练集 \n", - " eval_dataset=ms_val_dataset, # 验证集\n", - " cfg_modify_fn=cfg_modify_fn # 用于修改训练配置文件的回调函数\n", - " )\n", - " trainer = build_trainer(name=Trainers.image_classification, default_args=kwargs)\n", - "\n", - " # 进行训练\n", - " trainer.train()\n", - "\n", - " # 进行评估\n", - " result = trainer.evaluate()\n", - " print('Evaluation Result:', result)\n", - "\n", - " # 保存模型\n", - " save_model()\n", - "\n", - "if __name__ == \"__main__\":\n", - " train()\n", - " " - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "在当前的训练作业中,我们将使用PAI提供的PyTorch训练镜像,需要在镜像中安装ModelScope Library。通过在训练作业脚本目录下准备一个`requirements.txt`文件,可以在训练作业启动时,自动安装依赖的第三方库。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%%writefile train_src/requirements.txt\n", - "\n", - "\n", - "# 部分ModelScope依赖library由ModelScope Host,需要显式配置以下参数\n", - "--find-links https://modelscope.oss-cn-beijing.aliyuncs.com/releases/repo.html\n", - "modelscope[cv]==1.3.1" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "完整的训练作业脚本目录结构如下:\n", - "\n", - "```shell\n", - "\n", - "train_src\n", - " ├── finetune.py\n", - " └── requirements.txt\n", - "\n", - "```\n", - "\n", - "后续我们将通过PAI Python SDK将训练脚本提交到PAI执行。\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 提交训练作业到PAI\n", - "\n", - "SDK提供了High-Level的API,`pai.estimator.Estimator`,支持用户方便地使用镜像配合训练脚本,提交训练作业到PAI。以下代码中,我们将使用以上的训练作业脚本(`train_src`目录),配合PAI提供的PyTorch训练镜像,提交一个训练作业。\n", - "\n", - "对于如何使用SDK提交训练作业的详细介绍,可以见文档:[提交训练作业](https://help.aliyun.com/document_detail/2261505.html)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pai.estimator import Estimator\n", - "from pai.image import retrieve\n", - "\n", - "\n", - "# 使用PAI提供的最新的PyTorch GPU镜像\n", - "torch_img_uri = retrieve(\n", - " \"PyTorch\",\n", - " \"latest\",\n", - " accelerator_type=\"gpu\",\n", - ").image_uri\n", - "\n", - "# 使用训练配置信息,创建Estimator对象\n", - "est = Estimator(\n", - " command=\"python finetune.py\", # 训练作业的启动命令\n", - " source_dir=\"train_src\", # 训练作业脚本本地目录(绝对路径,或是相对路径)\n", - " image_uri=torch_img_uri, # 作业的镜像类型\n", - " # instance_type=\"ecs.gn6e-c12g1.3xlarge\", # 12vCPU 92GiB NVIDIA V100 × 1 (32GB GPU memory)\n", - " instance_type=\"ecs.gn7i-c8g1.2xlarge\", # 8vCPU 30GiB NVIDIA A10 × 1 (24GB GPU Memory)\n", - " base_job_name=\"vit-finetune\", # 作业名称\n", - " hyperparameters={ # 训练作业超参,用户可以通过环境变量或是读取配置文件的方式获取.\n", - " \"batch_size\": 128,\n", - " \"initial_learning_rate\": 1e-4,\n", - " \"epochs\": 2,\n", - " # 花朵数据集一共14个分类\n", - " \"num_classes\": 14,\n", - " },\n", - ")" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "通过`fit` API提交训练作业。当前示例中,我们在训练脚本中使用ModelScope的library去下载数据集。当用户需要使用自定义数据集时,可以通过`fit`方法传递相应数据OSS路径,训练作业会通过挂载的方式将相应的数据准备的执行环境中。\n", - "\n", - "```python\n", - "\n", - "est.fit(\n", - "\t# 用户的训练作业脚本可以通过环境变量 PAI_INPUT_{ChannelNameUpperCase} 获得数据的本地路径.\n", - "\t\"train\": \"oss:///train/data/path/\",\n", - ")\n", - "\n", - "```" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "est.fit()" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "训练作业执行成功之后,用户可以通过`estimator.model_data()`获取相应产出模型的OSS路径" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Step3: 部署推理服务\n", - "\n", - "PAI-EAS是PAI提供的推理服务部署平台,支持使用Processor或是镜像的方式部署推理服务。在以下的流程中,我们将使用微调获得的模型,使用镜像部署的方式部署一个在线推理服务。\n", - "\n", - "### 准备推理服务使用的代码\n", - "\n", - "镜像部署的模式,要求用户提供一个推理服务程序,他负责加载模型,提供HTTP API,以支持接受用户推理请求,调用模型处理推理请求,返回推理结果。在当前示例中,我们将使用[FastAPI](https://fastapi.tiangolo.com/)编写一个推理服务程序,加载以上训练作业输出的模型,在PAI创建一个推理服务。\n", - "\n", - "我们首先创建一个目录(`serve_src`),用于保存的推理服务程序代码。\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!mkdir -p serve_src/" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "我们准备的推理服务程序,支持用户通过HTTP POST发送的图片,然后调用ModelScope的推理pipeline获取预测结果,返回给到用户。\n", - "\n", - "> ModelScope 推理pipeline返回的结果中带有`numpy.ndarray`数据,需要我们通过自定义Encoder将其序列化。\n", - "\n", - "完整代码如下,我们将其保存到`serve_src`目录下,用于后续创建推理服务。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%%writefile serve_src/run.py\n", - "\n", - "import os\n", - "import io\n", - "import json\n", - "\n", - "import uvicorn\n", - "from fastapi import FastAPI, Response, Request\n", - "import numpy as np\n", - "\n", - "from modelscope.pipelines import pipeline\n", - "from modelscope.utils.constant import Tasks\n", - "from PIL import Image\n", - "\n", - "# 用户指定模型,默认会被加载到当前路径下。 \n", - "MODEL_PATH = \"/eas/workspace/model/\"\n", - "\n", - "class NumpyEncoder(json.JSONEncoder):\n", - "\n", - " def default(self, obj):\n", - " if isinstance(obj, np.ndarray):\n", - " return obj.tolist()\n", - " elif isinstance(obj, np.generic):\n", - " return obj.item()\n", - " else:\n", - " return json.JSONEncoder.default(self, obj)\n", - "\n", - "app = FastAPI()\n", - "\n", - "@app.post(\"/\")\n", - "async def predict(request: Request):\n", - " global p\n", - " content = await request.body()\n", - " img = Image.open(io.BytesIO(content))\n", - " res = p(img)\n", - " return Response(content=json.dumps(res, cls=NumpyEncoder), media_type=\"application/json\")\n", - "\n", - "\n", - "if __name__ == '__main__':\n", - " p = pipeline(\n", - " Tasks.image_classification,\n", - " model=MODEL_PATH,\n", - " )\n", - " uvicorn.run(app, host='0.0.0.0', port=8000)\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 创建推理服务\n", - "\n", - "我们将使用PAI提供的ModelScope推理镜像,使用以上的推理服务程序,创建一个推理服务。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pai.model import container_serving_spec\n", - "from pai.session import get_default_session\n", - "from pai.model import Model\n", - "from random import randint\n", - "\n", - "\n", - "# 使用PAI QuickStart提供的ModelScope的镜像创建推理服务\n", - "image_uri = (\n", - " \"registry.{}.aliyuncs.com/paiflow-public/quickstart:modelscope-1.2.0\".format(\n", - " get_default_session().region_id,\n", - " )\n", - ")\n", - "\n", - "\n", - "# 创建一个Model对象,他可以用于创建推理服务\n", - "m: Model = Model(\n", - " # 使用以上训练作业产出的模型\n", - " model_data=est.model_data(),\n", - " # 配置模型的推理配置,包括使用的镜像,使用的推理服务脚本,推理的依赖包等。\n", - " inference_spec=container_serving_spec(\n", - " source_dir=\"./serve_src/\",\n", - " command=\"python run.py\",\n", - " image_uri=image_uri,\n", - " requirements=[\n", - " \"fastapi\",\n", - " \"uvicorn\",\n", - " ],\n", - " ),\n", - ")\n", - "\n", - "\n", - "print(m.inference_spec.to_dict())" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "指定推理服务的名称,以及使用的机器实例规格,创建一个推理服务。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pai.predictor import Predictor\n", - "\n", - "\n", - "p: Predictor = m.deploy(\n", - " service_name=\"modelscope_vit_{}\".format(randint(0, 100000)),\n", - " instance_type=\"ecs.c6.xlarge\",\n", - " options={\n", - " # 推理镜像较大的镜像下,需要配置额外的磁盘空间\n", - " \"features.eas.aliyun.com/extra-ephemeral-storage\": \"40GB\"\n", - " },\n", - ")" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`model.deploy` 返回的`Predictor`对象可以用于向相应的推理服务发送请求,获得推理结果。\n", - "\n", - "\n", - "这里我们使用已经准备的一张花朵图片测试推理服务。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [ - "keep_output" - ] - }, - "outputs": [ - { - "data": { - "image/png": "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", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import io\n", - "\n", - "import requests\n", - "from PIL import Image\n", - "from IPython import display\n", - "\n", - "url = \"https://pai-sdk.oss-cn-shanghai.aliyuncs.com/resources/images/11563567033_b822736d84_c.jpeg\"\n", - "\n", - "data = requests.get(url).content\n", - "\n", - "display.display(Image.open(io.BytesIO(data)))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pai.predictor import RawResponse\n", - "\n", - "resp: RawResponse = p.raw_predict(data=data)\n", - "\n", - "print(resp.json())" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "在测试完成之后,我们可以将创建的服务删除。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "p.delete_service()" - ] - } - ], - "metadata": { - "execution": { - "timeout": 1800 - }, - "kernelspec": { - "display_name": "pai-dev-py38", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.3" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/source/tutorial/predict.rst b/docs/source/tutorial/predict.rst deleted file mode 100644 index bfc2ca8..0000000 --- a/docs/source/tutorial/predict.rst +++ /dev/null @@ -1,12 +0,0 @@ -=========================================== -模型部署 -=========================================== - - -.. toctree:: - :maxdepth: 1 - - model_deploy_container/model_deploy_container - async_inference/async_inference - modelscope_model_deploy/modelscope_model_deploy - huggingface_model_deploy/huggingface_model_deploy diff --git a/docs/source/tutorial/pretrained-model/pretrained-model.ipynb b/docs/source/tutorial/pretrained-model/pretrained-model.ipynb deleted file mode 100644 index 5e6ad2a..0000000 --- a/docs/source/tutorial/pretrained-model/pretrained-model.ipynb +++ /dev/null @@ -1,297 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# 使用PAI预置算法微调模型\n", - "\n", - "预训练模型(pre-trained model)是通过在大规模数据集上进行训练,从而学习到数据的特征表示的深度学习模型。因为模型是通过大规模的数据进行预训练,因而可以通过少量的数据集进行训练,避免从头训练模型的高额成本。在应用时,预训练模型可以被作为基础模型,然后在特定任务的有标注数据集上进行微调,从而适应特定任务的要求。\n", - "\n", - "PAI在公共模型仓库中,提供了不同领域,包括计算机视觉、自然语言处理、语音等的常见热门预训练模型,例如 `QWen`、`Bert`、`ChatGLM`、`LLama2`、`StableDiffusion 2.1` 等,并提供了相应的预置算法,用户仅需提供数据集,即可在PAI上完成模型微调训练。\n", - "\n", - "在本示例中,我们将以`Bert`模型为示例,展示如何使用PAI提供的预置算法对模型进行微调训练。" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "## 安装和配置SDK\n", - "\n", - "\n", - "我们需要首先安装PAI Python SDK以运行本示例。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "\n", - "!python -m pip install --upgrade alipai" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "SDK需要配置访问阿里云服务需要的AccessKey,以及当前使用的工作空间和OSS Bucket。在PAI SDK安装之后,通过在**命令行终端** 中执行以下命令,按照引导配置密钥、工作空间等信息。\n", - "\n", - "\n", - "```shell\n", - "\n", - "# 以下命令,请在 命令行终端 中执行.\n", - "\n", - "python -m pai.toolkit.config\n", - "\n", - "```\n", - "\n", - "我们可以通过以下代码验证配置是否已生效。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [ - "hide-output" - ] - }, - "outputs": [], - "source": [ - "import pai\n", - "from pai.session import get_default_session\n", - "\n", - "print(pai.__version__)\n", - "\n", - "sess = get_default_session()\n", - "\n", - "# 获取配置的工作空间信息\n", - "assert sess.workspace_name is not None\n", - "print(sess.workspace_name)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 查看PAI提供的预训练模型\n", - "\n", - "我们可以通过参数`provider`为`pai`,获取`PAI`公共模型仓库下的模型,其中包含了PAI提供的模型和从开源社区精选的模型。\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pai.model import RegisteredModel\n", - "\n", - "\n", - "data = [[\"ModelName\", \"Task\", \"Revision\"]]\n", - "\n", - "# 获取公共模型仓库'pai'提供的模型列表\n", - "for m in RegisteredModel.list(model_provider=\"pai\"):\n", - " revision = m.version_labels.get(\"revision\")\n", - " license = m.version_labels.get(\"license\")\n", - " task = m.task\n", - " data.append([m.model_name, task, revision])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from IPython.display import HTML, display\n", - "\n", - "display(\n", - " HTML(\n", - " \"{}
\".format(\n", - " \"\".join(\n", - " \"{}\".format(\"\".join(str(_) for _ in row))\n", - " for row in data\n", - " )\n", - " )\n", - " )\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 使用模型的预置算法微调训练\n", - "\n", - "通过`model_name`和`model_provider`参数,我们可以获取PAI提供的预训练模型(`RegisteredModel`对象),`RegisteredModel`对象包含了模型所在的OSS Bucket信息,以及模型的预训练算法配置。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pai.model import RegisteredModel\n", - "\n", - "# 获取PAI模型仓库中的bert-base-uncased模型\n", - "m = RegisteredModel(\n", - " model_name=\"bert-base-uncased\",\n", - " model_provider=\"pai\",\n", - ")\n", - "\n", - "print(m)\n", - "\n", - "# 查看模型的公共读OSS Bucket路径\n", - "print(m.model_data)\n", - "# 查看模型的训练算法配置\n", - "print(m.training_spec)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "获取 `bert-base-uncased` 模型的预置微调算法,以及算法的超参定义和输入数据定义。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import json\n", - "from pai.estimator import AlgorithmEstimator\n", - "\n", - "\n", - "# 通过注册模型的配置,获取相应的预训练算法\n", - "est: AlgorithmEstimator = m.get_estimator()\n", - "\n", - "# 查看算法的超参定义\n", - "print(json.dumps(est.hyperparameter_definitions, indent=4))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# 查看算法的输入输出通道定义\n", - "print(json.dumps(est.input_channel_definitions, indent=4))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# 默认的超参信息\n", - "print(\"before\")\n", - "print(est.hyperparameters)\n", - "\n", - "\n", - "# 配置超参\n", - "est.set_hyperparameters(batch_size=32)\n", - "\n", - "print(\"after\")\n", - "print(est.hyperparameters)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "模型默认自带了测试的训练数据,例如BERT模型提供的预置算法可以用于训练一个文本分类模型,默认提供了[情感分类数据集sst2](https://huggingface.co/datasets/sst2),可以直接用于模型的微调训练。\n", - "训练数据格式为一个`jsonline`文件,每一行为一个json对象,包含了`label`和`text`两个字段,分别表示文本的标签和文本内容。\n", - "\n", - "```json\n", - "{\"label\": \"negative\", \"text\": \"hide new secretions from the parental units \"}\n", - "{\"label\": \"negative\", \"text\": \"contains no wit , only labored gags \"}\n", - "{\"label\": \"positive\", \"text\": \"that loves its characters and communicates something rather beautiful about human nature \"}\n", - "...\n", - "...\n", - "\n", - "```\n", - "\n", - "用户可以参考以上的数据格式准备数据,当前示例中,我们将基于PAI提供的数据集对模型进行微调训练。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# 获取模型自带的训练输入\n", - "default_inputs = m.get_estimator_inputs()\n", - "\n", - "# 默认的算法训练输入,包含了算法使用的预训练模型,训练数据,以及验证数据。\n", - "print(json.dumps(default_inputs, indent=4))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "我们将模型配置的默认的数据集作为训练输入,使用模型预置的PAI算法提交训练作业。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "est.fit(inputs=default_inputs)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "在训练结束之后获取产出模型的OSS Bucket路径。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# 查看输出模型路径\n", - "print(est.model_data())" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "base", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.3" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/source/tutorial/pytorch_ddp/pytorch_ddp.ipynb b/docs/source/tutorial/pytorch_ddp/pytorch_ddp.ipynb deleted file mode 100644 index 86b853d..0000000 --- a/docs/source/tutorial/pytorch_ddp/pytorch_ddp.ipynb +++ /dev/null @@ -1,330 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# 提交PyTorch分布式作业\n", - "\n", - "\n", - "PAI支持用户提交分布式PyTorch训练作业,本文将介绍如何使用PAI Python SDK,以[PyTorch DDP(DistributedDataParallel)](https://pytorch.org/docs/stable/notes/ddp.html)模式提交分布式PyTorch训练作业。\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "## 安装和配置SDK\n", - "\n", - "我们需要首先安装PAI Python SDK以运行本示例。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [ - "skip-execution" - ] - }, - "outputs": [], - "source": [ - "!python -m pip install --upgrade alipai" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "\n", - "!python -m pip install pygments" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "SDK需要配置访问阿里云服务需要的AccessKey,以及当前使用的工作空间和OSS Bucket。在PAI SDK安装之后,通过在 **命令行终端** 中执行以下命令,按照引导配置密钥、工作空间等信息。\n", - "\n", - "\n", - "```shell\n", - "\n", - "# 以下命令,请在 命令行终端 中执行.\n", - "\n", - "python -m pai.toolkit.config\n", - "\n", - "```\n", - "\n", - "我们可以通过以下代码验证配置是否已生效。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import pai\n", - "from pai.session import get_default_session\n", - "\n", - "print(pai.__version__)\n", - "\n", - "sess = get_default_session()\n", - "\n", - "# 获取配置的工作空间信息\n", - "assert sess.workspace_name is not None\n", - "print(sess.workspace_name)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## PyToch 分布式作业介绍\n", - "\n", - "[PyTorch DDP(Distributed Data Parallel)](https://pytorch.org/docs/stable/notes/ddp.html)是PyTorch提供的分布式数据并行训练功能,支持模型在多台机器上进行并行训练,从而提高训练效率。\n", - "\n", - "PyTorch DDP基于多进程的方案实现,支持单机多卡模式和多机多卡模式。在单机多卡模式下,用户可以使用同一台机器下的多个GPU来加速模型的训练。在多机多卡模式下,可以将计算任务分配到多台机器上进行并行计算,加速训练速度。对于DDP的详细介绍,可以参考PyTorch的[官方文档链接](https://pytorch.org/docs/stable/notes/ddp.html)。\n", - "\n", - "\n", - "![PyTorch DDP](./resource/ddp.png)\n", - "\n", - "> PyTorch提供的`DataParallel`和`DistributedDataParallel`模块都支持数据并行训练,[PyTorch官方](https://pytorch.org/tutorials/intermediate/ddp_tutorial.html#comparison-between-dataparallel-and-distributeddataparallel)推荐不论是单机多卡还是多机多卡,都使用`DistributedDataParallel`模块进行训练。\n", - "\n", - "### 代码适配DDP改造\n", - "\n", - "使用PyTorch DDP进行分布式训练需要对原先的PyTorch训练代码(使用单机单卡)进行的修改,具体可以参考[PyTorch官方文档](https://pytorch.org/tutorials/beginner/dist_overview.html#torch-nn-parallel-distributeddataparallel)。\n", - "\n", - "主要包括:\n", - "\n", - "- 初始化分布式训练配置:\n", - "\n", - "需要在训练迭代开始之前完成训练环境初始化。\n", - "\n", - "```python\n", - "\n", - "from torch.distributed import init_process_group, destroy_process_group\n", - "\n", - "def ddp_setup()\n", - " init_process_group(backend=\"nccl\")\n", - "\n", - "```\n", - "\n", - "初始化需要指定机器之间的通讯方式,当使用GPU进行训练时,通常使用`nccl`作为通讯后端,而使用CPU训练时,使用`gloo`,详细的介绍可以参考PyTorch文档: [Which Backend To Use?](https://pytorch.org/docs/stable/distributed.html#which-backend-to-use)\n", - "\n", - "- 使用DDP封装模型:\n", - "\n", - "```python\n", - "\n", - "from torch.nn.parallel import DistributedDataParallel as DDP\n", - "\n", - "# model是原始单机单卡训练的PyTorch模型\n", - "model = DDP(model)\n", - "\n", - "```\n", - "\n", - "\n", - "- 修改DataLoader的采样方式:\n", - "\n", - "当使用DDP进行数据并行训练,不同的worker进程需要读取不同的数据分片进行训练。当不同机器上通过共享存储的方式使用同一份数据集时,可以使用`torch.utils.data.distributed.DistributedSampler`来对数据进行采样,从而保证不同的worker进程读取不同的数据分片。\n", - "\n", - "```python\n", - "\n", - "from torch.utils.data import DataLoader\n", - "from torch.utils.data.distributed import DistributedSampler\n", - "\n", - "train_sampler = DistributedSampler(\n", - "\ttrain_dataset,\n", - "\tshuffle=True)\n", - "\n", - "trainloader = DataLoader(\n", - "\ttrain_dataset,\n", - "\tbatch_size=args.per_device_train_batch_size,\n", - "\tsampler=train_sampler,\n", - "\tnum_workers=2,\n", - "\tdrop_last=True)\n", - "\n", - "```\n", - "\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "### PAI支持PyTorch DDP分布式训练\n", - "\n", - "PAI原生支持的PyTorch的分布式训练,当用户提交训练作业,指定作业类型为PyTorch训练作业时(`job_type=\"PyTorchJob\"`),PAI的训练服务会在机器节点上设置环境变量,包含作业机器数量,机器RANK,机器之间的通讯地址等信息。\n", - "\n", - "| 环境变量名 | \t描述 |\n", - "|:----------|:---------|\n", - "|MASTER_ADDR | Master机器节点的服务地址 |\n", - "|MASTER_PORT | Master机器节点端口号,如:23456 |\n", - "|WORLD_SIZE\t | 分布式作业的**机器节点总数**,例如提交的训练作业申请了4台机器,则WORLD_ISZE=4 |\n", - "|RANK\t| **机器节点的RANK**,例如启动了一个4个节点的作业,则各个机器节点的RANK分别为0,1,2,3 |\n", - "\n", - "\n", - "`PyTorch`提供了分布式训练启动工具,`torchrun`(PyTorch 1.1.0及以上版本) 和 `torch.distributed.launch`(PyTorch 1.1.0版本以下),支持训练作业的拉起。配合以上PAI预置的环境变量,我们可以便利得启动分布式训练作业。\n", - "\n", - "\n", - "\n", - "使用`torch.distributed.launch`拉起训练作业示例:\n", - "\n", - "```shell\n", - "\n", - "# for PyTorch<1.1.0\n", - "\n", - "python -m torch.distributed.launch \\\n", - "--nproc_per_node= \\\n", - "--master_addr=$MASTER_ADDR \\\n", - "--master_port=$MASTER_PORT \\\n", - "--nnodes=$WORLD_SIZE \\\n", - "--node_rank=$RANK \\\n", - " training_arguments...\n", - "\n", - "```\n", - "\n", - "使用`torchrun`拉起训练作业示例:\n", - "\n", - "```shell\n", - "\n", - "# for PyTorch>=1.1.0\n", - "torchrun \\\n", - "--nproc_per_node= \\\n", - "--master_addr=$MASTER_ADDR \\\n", - "--master_port=$MASTER_PORT \\\n", - "--nnodes=$WORLD_SIZE \\\n", - "--node_rank=$RANK \\\n", - " training_arguments...\n", - "\n", - "```\n", - "\n", - "用户需要修改` 以上的作业启动命令,同样适用于单机多卡的训练作业启动。\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 提交训练作业\n", - "\n", - "PAI Python SDK提供了Estimator的接口,用于提交训练作业,以下示例中,我们将通过Estimator提交一个PyTorch分布式训练作业。\n", - "\n", - "\n", - "- 准备训练代码\n", - "\n", - "PyTorch提供了多机多卡的[训练代码示例](https://github.com/pytorch/examples/blob/main/distributed/ddp-tutorial-series/multinode.py),在修改了模型和checkpoints保存路径后,我们既可以将其用于提交到PAI进行训练。\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# 通过以下代码查看准备提交的训练代码\n", - "!pygmentize train_src/train_multinode.py" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "- 提交训练作业\n", - "\n", - "我们将使用PAI提供的PyTorch 1.12版本的GPU镜像完成多机多卡的作业训练。使用`estimator.fit`提交训练作业之后,SDK会打印作业的控制台链接,用户可以通过控制台查看作业状态,日志详情等信息。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pai.estimator import Estimator\n", - "from pai.image import retrieve\n", - "\n", - "# 使用PAI提供的PyTorch 1.12 GPU镜像\n", - "image_uri = retrieve(\n", - " \"pytorch\",\n", - " framework_version=\"1.12\",\n", - " accelerator_type=\"GPU\",\n", - ").image_uri\n", - "print(\"Training Image URI: \", image_uri)\n", - "\n", - "\n", - "# 每一个机器实例的GPU数量,需要根据用户选择的机器型号(instance_type)进行修改\n", - "gpu_count_per_instance = 2\n", - "\n", - "# 训练脚本使用torchrun命令启动\n", - "command = f\"\"\"torchrun --master_addr=$MASTER_ADDR \\\n", - "--master_port=$MASTER_PORT \\\n", - "--nnodes=$WORLD_SIZE --node_rank=$RANK \\\n", - "--nproc_per_node={gpu_count_per_instance} \\\n", - "train_multinode.py --total_epochs 10 --save_every 5 \\\n", - "\"\"\"\n", - "\n", - "\n", - "# 提交训练作业\n", - "est = Estimator(\n", - " image_uri=image_uri,\n", - " source_dir=\"./train_src\", # 训练代码所在目录\n", - " command=command,\n", - " job_type=\"PyTorchJob\",\n", - " instance_type=\"ecs.gn6i-c24g1.12xlarge\", # 2 * NVIDIA T4 GPU\n", - " instance_count=2, # 机器实例数量\n", - " base_job_name=\"pytorch-ddp\",\n", - ")\n", - "\n", - "# fit方法提交训练作业,默认等待到作业执行完成\n", - "est.fit()\n", - "\n", - "\n", - "# 查看作业的输出模型\n", - "\n", - "est.model_data()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 参考:\n", - "\n", - "- PyTorch Distributed Overview: https://pytorch.org/tutorials/beginner/dist_overview.html" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "pai-dev-py38", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.16" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/source/tutorial/pytorch_ddp/resource/ddp.png b/docs/source/tutorial/pytorch_ddp/resource/ddp.png deleted file mode 100644 index ef2638dbbe4f61376d5f023dc3dbdda0463e0c4f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24040 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 Uny?uL4(KR=uLfAk4AAD6Dp#T5? diff --git a/docs/source/tutorial/pytorch_mnist/pytorch_mnist.ipynb b/docs/source/tutorial/pytorch_mnist/pytorch_mnist.ipynb deleted file mode 100644 index fa5de20..0000000 --- a/docs/source/tutorial/pytorch_mnist/pytorch_mnist.ipynb +++ /dev/null @@ -1,969 +0,0 @@ -{ - "cells": [ - { - "attachments": {}, - "cell_type": "markdown", - "metadata": { - "tags": [ - "skip-execution" - ] - }, - "source": [ - "# 基于PyTorch训练和部署MNIST图片分类模型\n", - "\n", - "PyTorch是一个非常流行的深度学习框架,提供了极高的灵活性和优越的性能,能够与Python丰富的生态无缝结合,被广泛应用于图像分类、语音识别、自然语言处理、推荐、AIGC等领域。本示例中,我们将使用PAI Python SDK,在PAI完成一个PyTorch模型的训练,然后使用训练获得的模型部署推理服务。主要流程包括:\n", - "\n", - "- Step1: 安装和配置SDK\n", - "\n", - "安装PAI Python SDK,并配置使用的AccessKey、工作空间以及OSS Bucket。\n", - "\n", - "- Step2: 准备训练数据\n", - "\n", - "我们下载一个MNIST数据集,上传到OSS上供训练作业使用。\n", - "\n", - "- Step3: 准备训练脚本\n", - "\n", - "我们使用PyTorch示例仓库中的MNIST训练脚本作为模板,在简单修改之后作为训练脚本。\n", - "\n", - "- Step4: 提交训练作业\n", - "\n", - "使用PAI Python SDK提供的Estimator API,创建一个训练作业,提交到云上执行。\n", - "\n", - "- Step5: 部署推理服务\n", - "\n", - "将以上训练作业输出的模型,分别使用Processor和镜像部署的方式部署到PAI-EAS,创建在线推理服务。\n", - "\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": { - "pycharm": { - "name": "#%% md\n" - } - }, - "source": [ - "## Step1: 安装和配置SDK\n", - "\n", - "我们需要首先安装PAI Python SDK以运行本示例。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "!python -m pip install --upgrade alipai\n", - "!python -m pip install pandas" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "SDK需要配置访问阿里云服务需要的AccessKey,以及当前使用的工作空间和OSS Bucket。在PAI SDK安装之后,通过在**命令行终端** 中执行以下命令,按照引导配置密钥、工作空间等信息。\n", - "\n", - "\n", - "```shell\n", - "\n", - "# 以下命令,请在 命令行终端 中执行.\n", - "\n", - "python -m pai.toolkit.config\n", - "\n", - "```\n", - "\n", - "我们可以通过以下代码验证配置是否已生效。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [], - "source": [ - "import pai\n", - "from pai.session import get_default_session\n", - "\n", - "print(pai.__version__)\n", - "\n", - "sess = get_default_session()\n", - "\n", - "# 获取配置的工作空间信息\n", - "assert sess.workspace_name is not None\n", - "print(sess.workspace_name)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Step2: 准备训练数据\n", - "\n", - "当前示例中,我们将使用MNIST数据集训练一个图片分类模型。为了支持训练作业加载使用,我们需要将数据上传到OSS Bucket上。\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "使用以下的Shell脚本,我们将MNIST数据集下载到本地目录data。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "vscode": { - "languageId": "shellscript" - } - }, - "outputs": [], - "source": [ - "%%sh\n", - "\n", - "#!/bin/sh\n", - "set -e\n", - "\n", - "url_prefix=\"https://ossci-datasets.s3.amazonaws.com/mnist/\"\n", - "# 如果以上的地址下载速度较慢,可以使用以下地址\n", - "# url_prefix=\"http://yann.lecun.com/exdb/mnist/\"\n", - "\n", - "mkdir -p data/MNIST/raw/\n", - "\n", - "wget ${url_prefix}train-images-idx3-ubyte.gz -P data/MNIST/raw/\n", - "wget ${url_prefix}train-labels-idx1-ubyte.gz -P data/MNIST/raw\n", - "wget ${url_prefix}t10k-images-idx3-ubyte.gz -P data/MNIST/raw\n", - "wget ${url_prefix}t10k-labels-idx1-ubyte.gz -P data/MNIST/raw\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "我们将使用PAI Python SDK提供的OSS上传API,将相应的数据上传到OSS Bucket上。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pai.common.oss_utils import upload\n", - "from pai.session import get_default_session\n", - "\n", - "sess = get_default_session()\n", - "data_path = \"./data\"\n", - "\n", - "data_uri = upload(data_path, oss_path=\"mnist/data/\", bucket=sess.oss_bucket)\n", - "\n", - "print(data_uri)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": { - "pycharm": { - "name": "#%% md\n" - } - }, - "source": [ - "## Step3: 准备训练脚本\n", - "\n", - "使用PyTorch训练模型,需要我们准备相应的脚本。这里我们以PyTorch官方提供的 [MNIST 示例](https://github.com/pytorch/examples/blob/main/mnist/main.py) 为基础,修改了数据加载和模型保存的逻辑,作为训练脚本。\n", - "\n", - "- 使用环境变量获得输入数据路径\n", - "\n", - "训练数据将被挂载到训练作业环境中使用,训练代码需要读取指定的路径获取训练数据。\n", - "\n", - "\n", - "```diff\n", - "\n", - "- dataset1 = datasets.MNIST(\"../data\", train=True, download=True, transform=transform)\n", - "- dataset2 = datasets.MNIST(\"../data\", train=False, transform=transform)\n", - "\n", - "+\t # 使用挂载到训练容器中的数据,默认为 /ml/input/{ChannelName},可以通过环境变量 `PAI_INPUT_{ChannelNameUpperCase}`\n", - "+ data_path = os.environ.get(\"PAI_INPUT_TRAIN_DATA\")\n", - "+ dataset1 = datasets.MNIST(data_path, train=True, download=True, transform=transform)\n", - "+ dataset2 = datasets.MNIST(data_path, train=False, transform=transform)\n", - "\n", - "\n", - "```\n", - "\n", - "- 使用环境变量获取模型的保存路径:\n", - "\n", - "用户需要保存模型到工作容器中的指定路径,PAI的训练服务将其才能够持久化保存模型到OSS Bucket上。默认要求用户需要将模型保存到环境变量 `PAI_OUTPUT_MODEL` 指定的路径下(默认为`/ml/output/model`)。\n", - "\n", - "\n", - "```diff\n", - "\n", - "- if args.save_model:\n", - "- torch.save(model.state_dict(), \"mnist_cnn.pt\")\n", - "\n", - "+ # 保存模型\n", - "+ save_model(model)\n", - "+\n", - "+\n", - "+ def save_model(model):\n", - "+ \"\"\"将模型转为TorchScript,保存到指定路径.\"\"\"\n", - "\n", - "+ output_model_path = os.environ.get(\"PAI_OUTPUT_MODEL\")\n", - "+ os.makedirs(output_model_path, exist_ok=True)\n", - "+\n", - "+ m = torch.jit.script(model)\n", - "+ m.save(os.path.join(output_model_path, \"mnist_cnn.pt\"))\n", - "\n", - "```\n", - "\n", - "PAI提供的预置[PyTorch Processor](https://help.aliyun.com/document_detail/470458.html) 在创建服务时,要求输入的模型是[TorchScript 格式](https://pytorch.org/docs/stable/jit.html) 。在本示例中,我们将模型导出为 `TorchScript格式` ,然后分别使用 `PyTorch Processor` 和镜像方式创建推理服务。\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "运行以下代码,创建一个训练脚本目录。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "pycharm": { - "name": "#%%\n" - }, - "tags": [ - "hide-cell" - ] - }, - "outputs": [], - "source": [ - "!mkdir -p train_src" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "将训练作业脚本保存到`train_src`训练脚本目录,完整的作业脚本如下:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "pycharm": { - "name": "#%%\n" - }, - "tags": [ - "hide-cell" - ] - }, - "outputs": [], - "source": [ - "%%writefile train_src/train.py\n", - "\n", - "# source: https://github.com/pytorch/examples/blob/main/mnist/main.py\n", - "from __future__ import print_function\n", - "\n", - "import argparse\n", - "import os\n", - "\n", - "import torch\n", - "import torch.nn as nn\n", - "import torch.nn.functional as F\n", - "import torch.optim as optim\n", - "from torch.optim.lr_scheduler import StepLR\n", - "from torchvision import datasets, transforms\n", - "\n", - "\n", - "class Net(nn.Module):\n", - " def __init__(self):\n", - " super(Net, self).__init__()\n", - " self.conv1 = nn.Conv2d(1, 32, 3, 1)\n", - " self.conv2 = nn.Conv2d(32, 64, 3, 1)\n", - " self.dropout1 = nn.Dropout(0.25)\n", - " self.dropout2 = nn.Dropout(0.5)\n", - " self.fc1 = nn.Linear(9216, 128)\n", - " self.fc2 = nn.Linear(128, 10)\n", - "\n", - " def forward(self, x):\n", - " x = self.conv1(x)\n", - " x = F.relu(x)\n", - " x = self.conv2(x)\n", - " x = F.relu(x)\n", - " x = F.max_pool2d(x, 2)\n", - " x = self.dropout1(x)\n", - " x = torch.flatten(x, 1)\n", - " x = self.fc1(x)\n", - " x = F.relu(x)\n", - " x = self.dropout2(x)\n", - " x = self.fc2(x)\n", - " output = F.log_softmax(x, dim=1)\n", - " return output\n", - "\n", - "\n", - "def train(args, model, device, train_loader, optimizer, epoch):\n", - " model.train()\n", - " for batch_idx, (data, target) in enumerate(train_loader):\n", - " data, target = data.to(device), target.to(device)\n", - " optimizer.zero_grad()\n", - " output = model(data)\n", - " loss = F.nll_loss(output, target)\n", - " loss.backward()\n", - " optimizer.step()\n", - " if batch_idx % args.log_interval == 0:\n", - " print(\n", - " \"Train Epoch: {} [{}/{} ({:.0f}%)]\\tLoss: {:.6f}\".format(\n", - " epoch,\n", - " batch_idx * len(data),\n", - " len(train_loader.dataset),\n", - " 100.0 * batch_idx / len(train_loader),\n", - " loss.item(),\n", - " )\n", - " )\n", - " if args.dry_run:\n", - " break\n", - "\n", - "\n", - "def test(model, device, test_loader):\n", - " model.eval()\n", - " test_loss = 0\n", - " correct = 0\n", - " with torch.no_grad():\n", - " for data, target in test_loader:\n", - " data, target = data.to(device), target.to(device)\n", - " output = model(data)\n", - " test_loss += F.nll_loss(\n", - " output, target, reduction=\"sum\"\n", - " ).item() # sum up batch loss\n", - " pred = output.argmax(\n", - " dim=1, keepdim=True\n", - " ) # get the index of the max log-probability\n", - " correct += pred.eq(target.view_as(pred)).sum().item()\n", - "\n", - " test_loss /= len(test_loader.dataset)\n", - "\n", - " print(\n", - " \"\\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\\n\".format(\n", - " test_loss,\n", - " correct,\n", - " len(test_loader.dataset),\n", - " 100.0 * correct / len(test_loader.dataset),\n", - " )\n", - " )\n", - "\n", - "\n", - "def main():\n", - " # Training settings\n", - " parser = argparse.ArgumentParser(description=\"PyTorch MNIST Example\")\n", - " parser.add_argument(\n", - " \"--batch-size\",\n", - " type=int,\n", - " default=64,\n", - " metavar=\"N\",\n", - " help=\"input batch size for training (default: 64)\",\n", - " )\n", - " parser.add_argument(\n", - " \"--test-batch-size\",\n", - " type=int,\n", - " default=1000,\n", - " metavar=\"N\",\n", - " help=\"input batch size for testing (default: 1000)\",\n", - " )\n", - " parser.add_argument(\n", - " \"--epochs\",\n", - " type=int,\n", - " default=14,\n", - " metavar=\"N\",\n", - " help=\"number of epochs to train (default: 14)\",\n", - " )\n", - " parser.add_argument(\n", - " \"--lr\",\n", - " type=float,\n", - " default=1.0,\n", - " metavar=\"LR\",\n", - " help=\"learning rate (default: 1.0)\",\n", - " )\n", - " parser.add_argument(\n", - " \"--gamma\",\n", - " type=float,\n", - " default=0.7,\n", - " metavar=\"M\",\n", - " help=\"Learning rate step gamma (default: 0.7)\",\n", - " )\n", - " parser.add_argument(\n", - " \"--no-cuda\", action=\"store_true\", default=False, help=\"disables CUDA training\"\n", - " )\n", - " parser.add_argument(\n", - " \"--dry-run\",\n", - " action=\"store_true\",\n", - " default=False,\n", - " help=\"quickly check a single pass\",\n", - " )\n", - " parser.add_argument(\n", - " \"--seed\", type=int, default=1, metavar=\"S\", help=\"random seed (default: 1)\"\n", - " )\n", - " parser.add_argument(\n", - " \"--log-interval\",\n", - " type=int,\n", - " default=10,\n", - " metavar=\"N\",\n", - " help=\"how many batches to wait before logging training status\",\n", - " )\n", - " parser.add_argument(\n", - " \"--save-model\",\n", - " action=\"store_true\",\n", - " default=False,\n", - " help=\"For Saving the current Model\",\n", - " )\n", - " args = parser.parse_args()\n", - " use_cuda = not args.no_cuda and torch.cuda.is_available()\n", - "\n", - " torch.manual_seed(args.seed)\n", - "\n", - " device = torch.device(\"cuda\" if use_cuda else \"cpu\")\n", - "\n", - " train_kwargs = {\"batch_size\": args.batch_size}\n", - " test_kwargs = {\"batch_size\": args.test_batch_size}\n", - " if use_cuda:\n", - " cuda_kwargs = {\"num_workers\": 1, \"pin_memory\": True, \"shuffle\": True}\n", - " train_kwargs.update(cuda_kwargs)\n", - " test_kwargs.update(cuda_kwargs)\n", - "\n", - " transform = transforms.Compose(\n", - " [transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))]\n", - " )\n", - "\n", - " data_path = os.environ.get(\"PAI_INPUT_TRAIN_DATA\", \"../data\")\n", - " dataset1 = datasets.MNIST(data_path, train=True, download=True, transform=transform)\n", - " dataset2 = datasets.MNIST(data_path, train=False, transform=transform)\n", - " train_loader = torch.utils.data.DataLoader(dataset1, **train_kwargs)\n", - " test_loader = torch.utils.data.DataLoader(dataset2, **test_kwargs)\n", - "\n", - " model = Net().to(device)\n", - " optimizer = optim.Adadelta(model.parameters(), lr=args.lr)\n", - "\n", - " scheduler = StepLR(optimizer, step_size=1, gamma=args.gamma)\n", - " for epoch in range(1, args.epochs + 1):\n", - " train(args, model, device, train_loader, optimizer, epoch)\n", - " test(model, device, test_loader)\n", - " scheduler.step()\n", - "\n", - " # 保存模型\n", - " save_model(model)\n", - "\n", - "\n", - "def save_model(model):\n", - " \"\"\"将模型转为TorchScript,保存到指定路径.\"\"\"\n", - " output_model_path = os.environ.get(\"PAI_OUTPUT_MODEL\", \"./model/\")\n", - " os.makedirs(output_model_path, exist_ok=True)\n", - "\n", - " m = torch.jit.script(model)\n", - " m.save(os.path.join(output_model_path, \"mnist_cnn.pt\"))\n", - "\n", - "\n", - "if __name__ == \"__main__\":\n", - " main()" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": { - "pycharm": { - "name": "#%% md\n" - } - }, - "source": [ - "## Step4: 提交训练作业\n", - "\n", - "`Estimator`支持用户使用本地的训练脚本,以指定的镜像在云上执行训练作业。通过`Estimator`,我们将以上准备的训练作业脚本提交到PAI,使用PAI提供的PyTorch镜像执行训练任务。\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [], - "source": [ - "from pai.estimator import Estimator\n", - "from pai.image import retrieve\n", - "\n", - "\n", - "# 使用PAI提供的PyTorch的GPU训练镜像\n", - "image_uri = retrieve(\n", - " \"PyTorch\",\n", - " framework_version=\"1.8PAI\",\n", - " accelerator_type=\"GPU\",\n", - ").image_uri\n", - "\n", - "print(image_uri)\n", - "\n", - "\n", - "# 配置训练作业\n", - "est = Estimator(\n", - " # 训练作业启动命令\n", - " command=\"python train.py --epochs 5 --batch-size 256 --lr 0.5\",\n", - " # 需要上传的代码文件\n", - " source_dir=\"./train_src/\",\n", - " # 训练作业镜像\n", - " image_uri=image_uri,\n", - " # 机器配置\n", - " # PAI的训练服务支持机器实例类型请见文档:[公共资源组实例和定价](https://help.aliyun.com/document_detail/171758.html?#section-55y-4tq-84y)\n", - " instance_type=\"ecs.gn6i-c4g1.xlarge\", # 4vCPU 15GB 1*NVIDIA T4\n", - " # 训练作业的Metric捕获配置\n", - " # 训练服务支持从训练作业输出日志中(训练脚本打印的标准输出和标准错误输出),以正则表达式匹配的方式捕获训练作业Metrics信息。\n", - " metric_definitions=[\n", - " {\n", - " \"Name\": \"loss\",\n", - " \"Regex\": r\".*loss=([-+]?[0-9]*.?[0-9]+(?:[eE][-+]?[0-9]+)?).*\",\n", - " },\n", - " ],\n", - " base_job_name=\"pytorch_mnist\",\n", - ")" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "`estimator.fit`方法将用户的训练作业提交到PAI上执行。任务提交之后,SDK会打印作业详情页链接和训练作业的日志,等待作业执行结束。\n", - "\n", - "当用户需要直接使用OSS上数据,可以通过`estimator.fit`方法的`inputs`参数传递。通过`inputs`传递数据存储路径会被挂载到目录下,用户的训练脚本可以通过读取本地文件的方式加载数据。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# 使用.fit方法提交训练作业\n", - "est.fit(\n", - " inputs={\n", - " # 训练作业的输入数据,每一个Key,Value对是一个Channel,用户可以通过环境变量PAI_INPUT_{ChannelNameUpperCase}获取对应的数据路径\n", - " # 例如以下的train_data,训练的脚本中可以通过`PAI_INPUT_TRAIN_DATA`获取数据挂载后的路径.\n", - " \"train_data\": data_uri,\n", - " }\n", - ")\n", - "\n", - "# 训练作业产出的模型路径\n", - "print(\"TrainingJob output model data:\")\n", - "print(est.model_data())" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": { - "pycharm": { - "name": "#%% md\n" - } - }, - "source": [ - "## Step5: 部署推理服务\n", - "\n", - "在训练作业结束之后,我们可以使用`estimator.model_data()`方法拿到训练作业产出模型的OSS路径。下面的流程中,我们将训练产出的模型部署到PAI创建在线推理服务。\n", - "\n", - "部署推理服务的主要流程包括:\n", - "\n", - "- 通过`InferenceSpec`描述如何使用模型构建推理服务\n", - "\n", - "用户可以选择使用Processor或是自定义镜像的模式进行模型部署。以下示例中将分别使用两种方式部署获得的PyTorch模型。\n", - "\n", - "- 通过`Model.deploy`方法,配置服务的使用资源,服务名称,等信息,创建推理服务。\n", - "\n", - "对于部署推理服务的详细介绍,可以见: [文档:部署推理服务](https://pai-sdk.oss-cn-shanghai.aliyuncs.com/pai/doc/latest/user-guide/model.html)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Processor 模式部署\n", - "\n", - "[Processor](https://help.aliyun.com/document_detail/111029.html) 是PAI对于推理服务程序包的抽象描述,他负责加载模型并启动模型推理服务。模型推理服务会暴露API支持用户进行调用。\n", - "\n", - "PAI提供了预置[PyTorch Processor](https://help.aliyun.com/document_detail/470458.html),支持用户方便地将TorchScript格式的模型部署到PAI,创建推理服务。\n", - "\n", - "以下示例代码中,我们通过PyTorch Processor将训练产出的模型部署为一个推理服务。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "pycharm": { - "name": "#%%\n" - }, - "tags": [] - }, - "outputs": [], - "source": [ - "from pai.model import Model, InferenceSpec\n", - "from pai.predictor import Predictor\n", - "from pai.common.utils import random_str\n", - "\n", - "\n", - "m = Model(\n", - " model_data=est.model_data(),\n", - " # 使用PAI提供的PyTorch Processor\n", - " inference_spec=InferenceSpec(processor=\"pytorch_cpu_1.10\"),\n", - ")\n", - "\n", - "p: Predictor = m.deploy(\n", - " service_name=\"tutorial_pt_mnist_proc_{}\".format(random_str(6)),\n", - " instance_type=\"ecs.c6.xlarge\",\n", - ")\n", - "\n", - "print(p.service_name)\n", - "print(p.service_status)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": { - "pycharm": { - "name": "#%% md\n" - } - }, - "source": [ - "`Model.deploy`返回的`Predictor`对象指向创建的推理服务,可以通过`Predictor.predict`方法发送预测请求给到服务,拿到预测结果。\n", - "\n", - "我们使用`numpy`构建了一个测试样本数据,发送给推理服务。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [], - "source": [ - "import numpy as np\n", - "\n", - "# # 以上保存TorchScript模型要求输入为 Float32, 数据格式格式的形状为 (BatchSize, Channel, Height, Width)\n", - "dummy_input = np.random.rand(2, 1, 28, 28).astype(np.float32)\n", - "\n", - "# np.random.rand(1, 1, 28, 28).dtype\n", - "res = p.predict(dummy_input)\n", - "print(res)\n", - "\n", - "print(np.argmax(res, 1))" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": { - "pycharm": { - "name": "#%% md\n" - } - }, - "source": [ - "在测试完成之后,可以通过`Predictor.delete_service`删除推理服务。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "pycharm": { - "name": "#%%\n" - } - }, - "outputs": [], - "source": [ - "p.delete_service()" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 镜像部署\n", - "\n", - "Processor模式启动的推理服务性能优越,适合于对于性能较为敏感的场景。对于一些需要灵活自定义的场景,例如模型使用了一些第三方的依赖,或是推理服务需要有前处理和后处理,用户可以通过镜像部署的方式实现。\n", - "\n", - "SDK提供了`pai.model.container_serving_spec()`方法,支持用户使用本地的推理服务代码配合PAI提供的基础镜像的方式创建推理服务。\n", - "\n", - "在使用镜像部署之前,我们需要准备模型服务的代码,负责加载模型、拉起HTTP Server、处理用户的推理请求。我们将使用Flask编写一个模型服务的代码,示例如下:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# 准备推理代码保存目录\n", - "!mkdir -p infer_src" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%%writefile infer_src/run.py\n", - "\n", - "\n", - "import json\n", - "from flask import Flask, request\n", - "from PIL import Image\n", - "import os\n", - "import torch\n", - "import torchvision.transforms as transforms\n", - "import numpy as np\n", - "import io\n", - "\n", - "app = Flask(__name__)\n", - "# 用户指定模型,默认会被加载到当前路径下。 \n", - "MODEL_PATH = \"/eas/workspace/model/\"\n", - "\n", - "device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n", - "model = torch.jit.load(os.path.join(MODEL_PATH, \"mnist_cnn.pt\"), map_location=device).to(device)\n", - "transform = transforms.Compose(\n", - " [transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))]\n", - ")\n", - "\n", - "\n", - "@app.route(\"/\", methods=[\"POST\"])\n", - "def predict():\n", - " # 预处理图片数据\n", - " im = Image.open(io.BytesIO(request.data))\n", - " input_tensor = transform(im).to(device)\n", - " input_tensor.unsqueeze_(0)\n", - " # 使用模型进行推理\n", - " output_tensor = model(input_tensor)\n", - " pred_res =output_tensor.detach().cpu().numpy()[0] \n", - "\n", - " return json.dumps(pred_res.tolist())\n", - "\n", - "\n", - "if __name__ == '__main__':\n", - " app.run(host=\"0.0.0.0\", port=int(os.environ.get(\"LISTENING_PORT\", 8000)))\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "通过`pai.model.container_serving_spec`,我们基于本地脚本和PAI提供的`PyTorch`镜像创建了一个`InferenceSpec`对象。\n", - "\n", - "- 模型服务的代码和启动命令:\n", - " \n", - "用户指定的本地脚本目录source_dir会被上传到OSS,然后挂载到服务容器(默认到 /ml/usercode目录)。\n", - "\n", - "- 推理服务镜像:\n", - "\n", - "PAI 提供了基础的推理镜像支持用户使用,用户可以通过`pai.image.retrieve`方法,指定参数`image_scope=ImageScope.INFERENCE`获取PAI提供的推理镜像。\n", - "\n", - "- 模型服务的第三方依赖包:\n", - "\n", - "模型服务代码或是模型的依赖,可以通过`requirements`参数指定,相应的依赖会在服务程序启动前被安装到环境中。\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pai.model import InferenceSpec, container_serving_spec\n", - "from pai.image import retrieve, ImageScope\n", - "\n", - "torch_image_uri = retrieve(\n", - " framework_name=\"pytorch\", framework_version=\"1.12\", accelerator_type=\"CPU\"\n", - ").image_uri\n", - "\n", - "inf_spec = container_serving_spec(\n", - " command=\"python run.py\",\n", - " source_dir=\"./infer_src/\",\n", - " image_uri=torch_image_uri,\n", - " requirements=[\"flask==2.0.0\"],\n", - ")\n", - "print(inf_spec.to_dict())" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "使用训练作业输出的模型,以及以上的 InferenceSpec,我们将通过 Model.deploy API部署一个在线推理服务。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pai.model import Model\n", - "from pai.common.utils import random_str\n", - "import numpy as np\n", - "\n", - "\n", - "m = Model(\n", - " model_data=est.model_data(),\n", - " inference_spec=inf_spec,\n", - ")\n", - "\n", - "predictor = m.deploy(\n", - " service_name=\"torch_mnist_script_container_{}\".format(random_str(6)),\n", - " instance_type=\"ecs.c6.xlarge\",\n", - ")" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "我们准备一张 MNIST 测试图片,用于发送给到推理服务。\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "pycharm": { - "name": "#%%\n" - }, - "tags": [ - "keep_output" - ] - }, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAAAAABXZoBIAAABvklEQVR4nF2SO2tUURSFv73PPufMMzGG+EALhZhoaxBs7EWwCoiNhfgbLERrQSwshfwA7SyTIIJgJVgpBBEhEZuQQsIgE/XeubMtZu4wk9UuztrrcWAMEwACGQQSs9CUFARyApuhogKICSFgzZFaTZaoigwHolSo2GD6pYTRUTOoZbUmvXJodhoDMUJDwqyflAGYA6vNyrSntfXbq38e95a3PwQtAVSwjELm/v6hb30ZlL6Rwlg2gSLQvb7vn9bS3JuhP6EzFmsIEoF77ptB853Sd05oitRJrcXp5/5zY9Gwb95fR8dlKiCZh799K2Hdm4e7T1s6ZTIiZ/aq11hc2nF/Hw0h1wEj4awX50892vS/v/q3YIl5JrK0WgfeL32wu+c/WJyZKsLcaq/4+uziuXfFi0QkyuhsAIWwQIb5G+4PkPZk6gQSgNCE7mXvrUAgjQcx0Fh7Pzn0DgJGBLAKvHINPgj56GpRNigKnBLAXHDHpaI6YjkXw3/lZCrFAajoBvhcdFMJUk11FEzHE3/3a22yYFPsKK0m7vrHS3Qjx2EE48rLg7dEYvsYqSKCXnjVX2lrg9kPJpOiAVnIAP8B0Kx+GvoyGWQAAAAASUVORK5CYII=", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "\n", - "!pip install -q pillow\n", - "\n", - "\n", - "import base64\n", - "from PIL import Image\n", - "from IPython import display\n", - "import io\n", - "\n", - "\n", - "# raw_data是一张MNIST图片,对应数字9\n", - "raw_data = base64.b64decode(b\"/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/wAALCAAcABwBAREA/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/9oACAEBAAA/APn+rVhpmoarP5GnWNzeTYz5dvE0jfkoJovNMv8ATmK3tjc2zByhE8TIQw6jkdR6VVq9oumPrWuWGlxyLG95cRwK7dFLMFyfzr3aXwp4ltAfB3gWwudI01JNuoa7eZhku5AMHafvFOw2Dn6ZJ4z4yeLk1HUbXwrZSSy2Oh5heeaQu88wG1mLHk4wR9c+1eXUqsVYMpIIOQR2r1D4QazqOs/FnSG1fVLi9ZI5vL+2TNKc+U2ApYnB7/hXml5LLNfXEsxLSvIzOSMEsTk1DRVnT7+60vULe/spmhureQSRSL1Vh0NWNd1mXX9ZuNUuLe2gmuCGkS2QohbABbBJwTjJ9yelZ1f/2Q==\")\n", - "\n", - "im = Image.open(io.BytesIO(raw_data))\n", - "\n", - "display.display(im)\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "推理服务使用 HTTP 请求体内的数据作为输入的图片,SDK 的 `raw_predict` 方法接受 bytes 数据类型的请求,通过 POST 方法,在请求内带上用户推理数据,发送给到推理服务。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import json\n", - "from pai.predictor import RawResponse\n", - "\n", - "resp: RawResponse = predictor.raw_predict(data=raw_data)\n", - "print(resp.json())\n", - "\n", - "print(np.argmax(resp.json()))" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "测试完成之后可以删除服务。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "predictor.delete_service()" - ] - } - ], - "metadata": { - "execution": { - "timeout": 1800 - }, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.16" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} diff --git a/docs/source/tutorial/stable_diffusion_lora/resource/dreambooth.jpeg b/docs/source/tutorial/stable_diffusion_lora/resource/dreambooth.jpeg deleted file mode 100644 index 6f60f556e64b53b330d528c3059feb34475237e2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 617270 zcmd42Wn5Hm+bFtVXr)0yWKbGGK{|(!?nXpO=|*W7Qb1{tl9UD!kVaBkK~lO?K%`@s z+ROj*zWaI4K6{_@Veb!n&2L!utaZoL_Z2tTS?nr6p{As&1mNHR01o&8u|5vzRK;KQBvbFZcg!|4;7Vycg4dX9s{u zzW>Sl|G!7XHZQ!a!A?iu_oXMeI0&o_*ru`jSKIe*+v;EK*uQO013d+>Pd?b@vimP> ztN+sWdFlQV>@)bczqPyP-}WNdmU3}^^>?km*I$TlzHl|v2k$1}mkIC!^Z;c*9&G;~ zz5Y#4w+{dyb`AiL#D71t$^?L>NC2Rp|MxStd;lPi0D#6(cS}#pe}KUQ|Ki%(0>DWr z0FXWb0O~OSAbI+qfc|^zzj^V$z?B6Y!vx~#27c@SXW#|E0;mG6fHm*{Y&`<_0Rcb+ zy8tMF;_<(C{hj20Zw7EQwhN#n!udhq3BjQRa4B&hlsMR4016_3kN;P&|NBKiNJNYa z!NVuPxdC?YrvPvu5L|p*JP1A>0Um%WiGvHF#KXT!AOH{&QOQy365q4*pm`KZ%POB# zKypj3mX2Pqd;Exj&B}9)k)4B+>sVAQEcwId32q@_1r>d-uOdpyCQp2$)5RqoD;gLY zzp%Bl_wjrEu2|K``sFL{$f%6M(uU@qDR6COP}%SxAngAgHUNZ+00$R@kOX}1L?5%IJlGmTsJYvDJ*JW^YT^1yTbafJyYvSFN!uIdp8@1ZL#yf zO$ZK1HV7pE0}eOBh?pp3SHlSLD8yJoyYa|~Sls0x{}(;%CwokI_te(6rw-TA&-|+t z8!2Ydg5p_>m*dm#`x7M7p|5S~5cHQA_|EllJj+Pmv!}nt46B|&_f}Mxm^2(qx4w>N zFxhZXbs^}xn@bu3yhm2uJxa}4FJik^H?%sG5Aevyg!VUs$7KJ%evXF#`JvtYIe`iD zg1GQ)at8O`=Vun7U2Ajuzq%oD@*%BxbxJ(zIa-Ngql~XkVD?P4S5Aom|FHzae+=b+ z9t$;HIE2LF-<(3tCKTbNe=u`A`i~JqTP93Q4l{%3e5PGUc$@?RTJH$p)i@b0VMZXa z9)zf=@r03F5vfW@>^nkQ))dCwRLk$Ctvx>~lyDx}?}XmKbR(7S zm@i?0R2GlAza1f!$j*}pGQz);5gK2@w}_Z5Znbct^<4*K(#hcbwd~n1H>VJYuMdAWg&D{cnT}>$ra;q4l&?5=IzJ^MHKDHdIn}H7@^u6y|;=F~)^@tAdf=UzeO9vHceR z>OdKWAl$NNqRja3%D@WXfO;SZjD|B{tQSCqu;@^1Steq#{U7v&f3{dQn5AIKp(;#d|Imu^AHg8}7h{w5{cS2cy^?~FY

W1iEaJ|Hk^k#J;rEO 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ⅇfvs_WBG%2}i; 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≫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 z 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 z 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 z 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


{hP_0T_7VsN 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�QHFsP>hq3GP_Y2trT17Xh0((*C@RDvV6F;VNdW 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�dQbgZ z*~6#5^Zx)P^Yj$|0Cwi!BovK{jcWe@P%qrhdw1m(4RO@WNBtDP7dZa_-45o4F&Iy+ z=jNQ?5FpI)pZ78Ec>KF}W`U3*fByiQ8=ig;lE00Oz;Z^$#vk>opZ(S7*~g;pfc#{^JKzD32N@s3F8Kcd+5Mb5Px@gaAMV=R5&r;DvATYL{{V{~ z9c$FGjGu)!#mpvTkM#@Ye3SJ50Idvoh(7b4)1UVKk60C@JEe}O0&3Wn;^8WxkLDaw2>UF^=Dm?}BHAIn&f7VBD zoCy#Fl43Fb=1uS6Z|(h?URa8M`>S{lyeIzcox~Iy9~=JAYx0}k&CWgk`gISA~3JDJiZH&B0U50*ZX2nUQG;ct7Cmj2(_sV{cKYUaO$ zcI{eT@PE5gZ~<@ZTww!4K~MYd(k-i*eNU(u_)>gai8}>!AI%5^kWbI+os8tRC(bt5 ze+!%2!phtGIGosG3$dl}lzu|lH~`>(8lKObgRya8-M%2@!T$idxEW|WM9TjFX0w-^ z`?Yl`@n7c;I67SFbEa)G&PMtZM=ogknB<1GnT>&zFeA?CDdb%$+NX-OMvFGpD4d zi6~P9%?Ti(H3s2{@Q=04@!9+Pznf=zHe8kA@LNT46)H@^>Ohr;mnec=U(|v_DAA*> zJ}!~XAaFqgf(RTCK;VK01Q0kNfx!e02*Ci*!$9j+Khaf)sE`J|wn+p0T60yW{r)qz z--!Hr$dcm6=zDoq*+P29H8HK5)FX0Gs4SIQ!`P3>2rJ6)~Kn{vf|B7-Sv3GZnPCB2Hqr}DNq39c-(zH z=gB-X_pY*nD0Is|Pj;{osU<`JJpE}IN?VBuR3u5*9GUkS&y0=!xZ^x3AhjQ+T#e%j zJVhqV^y}gJ#ceobWR(ds{4umn#XuQ>l0@;p^F1_d0+I(2beH007qzY}pDt05U0O!! z`9LrLk_JR$bFni9AnbV6N>~N-r3cai5&_{g6g=P6KJXY)mas>7NZ+^+2*Jkx0OjXC z+$Mmf+^a>^Y7!0_`&!{_+I(>dbB8l7L=yWOY&m+ zXXVZUNiGDsclk&^UM zqcJ4@zS!eZlPnW=0Ms{b&u_dkKu}l!o*U-CbMD_;!G86rteSqA`pdn}mWhf~th$$0 zJIOQYfPe~$mZ$+K1d?MD#C{_kDnKDq2N9qg{$39GhJS}Gk$}rZ5G-@p{jHMB`R^{D7dgy0YW=Wm>C>LV>Yz+A;<-V znANRMndWkba&U5qAQ}Q0?^eo;AFJsR@l&iXdc&D_Z81|{SE$!g*iulC?R8*M$x53_ z8Shg_z#$oN|XTt4$W|zBWO*T#WGnSlT$z# zH?4BiD?e>vB`gG}8e9%pv(R&+@&Xs$UG%Z2t(L2GvtR03TB~ibezQ~-mR9{+l`9~) z?PXKw4WzgPI2mn8RL^jpCFO47z3Qh-UkkN3a<)|(c$kf!w_FrUPsB}2-j4b6>ullS z>D?!0vC}Zsw%*H3)3%n9q7Qd!Km|(5QlemzuF#@MjfB`Rn?6n!Q2`;?d{wX3?Uz20 zbMY&hzBMU;$cO3S$)MC=kQ+*b9~mApJYps%961SEYMQ&T(^?AGlUA{)P*AI+3+HzY z&!3c5@mPd~ZVua@<(Qe6#BcAzb)ssCaYvwV8lEw-R1!xEdZn&k^^9>aNf|%!8xmy4 zoRhyAGLg;Mz^k}%;X~mI%BWgirHBuq`Pw}`NrQ>bz{&O_?~lLKXN0LQ{X+R?Z!ze` z?zE+i##9;PdVQk_aon@qQURF;MowgtiT3a|*d7{AQy>(V;&8xeY`#{FnOs3iED{cx z^o&KYhe-YDR!Jd70oceetN}Uy051s3m+u@MIu3gE&Su`yWQl+4U9F5Pk z40$z={pi@ z8KR-SJ91&?*Hk;I%{l|E)1`nRp(!%85R#OrWm`ewrWNA~K=?`ctIL)2tp}7$$ineq zRH6C6r7V80u8^`;IIhu7)ond93rT3yE%Mlmr3IoC5*EDnkVKLJkYk7Fm+-B~A*oRA z#q)7-^=iWdB5Q)g6%)lu7BtM?^@iih{%ZaQQtPW0rKn2|X}iT$x#w2;7F?zXHB^-n z;0b*$Q7yOeB`EJF@+ukOCz&(mAxb46{3A!ct{jgTc{>7q!WN&*d*@#Yr+!Bk7^leD_7LCYF*a6$m)}3P${Y3PisZr`(?@pfgJs(ZFdUnv25?e~z z4J~Ua1wG3ScCJY6Z(*Ru;sGg2D@zjRM}CgkUXid^NDM${Vh&lbv4SR}=)X}~)7_@t zqq1O&%r)~lQ0j=+k~VO+LffJz)DrO4rSv>1k3~wY)PkEK&}-F@jcJ&r-UT7 zZci#U+ZS=RE5O9RczAN3Wh2cne1IU{{RX909a{6cLk%=B_>mo2NQ7D<`tk?^xq`^S~PaYmzO4`WF5-^-?G=tmQ$aDdi56le%X|a?N*Y>s_LP)VlOB zOn(f*TP`gwlF)F1l`Xc^;q7V)B}9SU1Zpu#Lxn7@z{rY?XlbR$g%K)pi-dsY^f$X& zwYSa{EicTa*J@o?Yi^;swArpS_OcmNveV|=)oPt77)oAhrLxd51qi^YPTsZG^568$UUk+Snu04YHaCZoNz zgp>1yfT$D1))Nv|J~Kc&>iP@Bbo>fL+9ku?*}?Ap^XAoV8dI5eUTSKq#T~YisdbhI zRY_k^Y1|a3aita%(WqGMQj(_6r`n}UeX5M*NwWga!W;%-;C1u)Ls_!ZC7dt?h^?40 zyXW6HTpP78mh#VXllh5A3KDa)AGpal8=U9?B7oR9A1-mK$~ZD(Zyd9WYqzioDFrD= zQJ94Z?M^4mt8f&1K`|UZLHI)(KZ(Du2xiY+Jf4y2s8&%3SRfGvQQiPMU`QfyCOjC& z0|y67cY+lzOVbg~E>aarl%8g2C3hJn6RPcGoroCD;BOvyd4c}`eMzom`F`$AKCs&q zPr6tdd|ZVa#yS~R$LxR0i_GuyQ-AMk{{YP~5}S^r=N#Vi)73{gxv}|k%)NY}BLa10 z-XKVsAP`9=N!SSE=J&Kej)C%B$q5(>m2=jc&w?q>E51PDGM!LHG?Hb zC$!Sjl1ZP;qa^zPB0TzM_;s37wRXB_-^iw{lEtd_uw&Xe2eqU=$-xHVBi?YAV&y=y za_>&2IoqYtjkuJLNC+S&8@@nCg|-+)Vs1!jSVCP`S{*Vz)}*B1udBM%AI1YN%(`FDi?eB{R@+it>M1FGShDJbvcr`P+iwk!6-@K4BsD>GYE^DqiCc_S)1b5>wx-Va$Rz>IJ$0l? zY}`PXFbS1KE@^RIV7`tkQ4safk6sj-$57R2i}ibKRZvybQPwc~&9MD7Z7Yu@#vNfw zQ4TiiN}+jxJIk$hp4U>Os-rqiBL?aq@Pns3#-1X!c(hHIhn#?L>{P70*t-|~@`(-S zhdgZhH%>css;fY0bmivKg(N9$p5DTNAd;q(Pl4Q&k32-;F;gazgddHn!&7@w`f4aE z3t={UD0{#O0`3~WHt$+NEP+O|E40`7h)3B0JDMo;Rb8pj_`DYW4$n6B?l(Mx|bE|sgcdzWL zs;GDZrrcUb?if#cl9t*j?Gc}pO}uHWm`glA^E=!hRA1Sg~_J^yux8 ziuBhCM*_-Rxgz4k9K0`D#{_>9dqVHsVAk1rePVUWhxHn@O-03&HvLBxQB7XOL8oB$sdV)==h$$er)j}?vT>lf zlwB&l1vCJ13^Hij8L{!$00jAJMyXLWJ^7J;7-H4h1(w=TC@CN&LO>v5WSH2DkUpb~ zd(|mJi{X14`s6%CqC4HG1Svw9kzAXvluD1nI?9Viz2`cTkdp3|(uf8kLT`69gD?b~ z0uR(5ubqF0Rqq?#vAN&GVN z69=@xBilLgus$QpQb{Rt8QPvdMvmr!1jPVo7!IVp4~Kh2?EqrUZvapJBhY;AwvbPd zGGxc!iHs@$L#v(ip%lOC=F=Ne05mKIH*W{mlo~0Ppy8I2*HK+qA9b+XN5z)wRY}3bJ4=AB*7+KZjcn9C}_n>zPj02_<=9exN?JR(sLAj zB)`kiBoDODh>6a9Ex06&p@?d_CAhVntIPC{DgOWwwB4PZue_e!36}o=HvQr`pQ-t! zMC_;le<8lWO#So6UqBT(=Sz3Z`}NrQWPXkuG4m4Dx%X`w;XK_vi(aA!0o++cA8O&!YkZk@a|>JNUG2364e6v#P;X!(cP)@ zuo(Gc6R|dd67e!tM+~%r!9m>Al4t`k1?dboRJ*Afz5}Z1Dh1k=v^8H+DZMJ^*<~Gw zskzazlEdyRu_^ttXEipO@P$;BQIx4Gi@@(5BM7hT?YS*-r_10fZCE3w|74kFBONxC*CUvl9Z}RHBwn;h?jacq0ON4rymPL6;$t`WhpB|%&9IR zZK_0}Kol$yC?heDW(eZJK3)rI;O5CvW#VK`04XI&uQnyzWHr_(ST*f!+1fJkauquP zj>0YFObA)vQ~Xt63xL-lucS#YBYL*B>01Vl<&|Zoy>FJ+XJ+JtHr=X~4TtI}-K%)1 zDWq&Pgwi%jJu>RrZ6E`WEqg-T%x1#I&c;^4B49+-09LdqApkR) zfIC$5caFKd`~AE7rL%EcQyGfGw1l5?B@kR=JK zR)5J@y=v*owPQ+TVDjw_l3ED<{{UKki4+%qP?4XNAA~BHG3_%3Gs9Ck336s6J%{Jzn~!jm$t37V0?$%eyLX2F0Ipcl%+kK0aINkD%I+`Ptc(Pb zV5|0pdK4fI+zA3tY2hij>@N@IBoYB+nitpga6H^e*qcdnP@^d+EUFo(85%o|&#xg8 zjqsejY1?Nld5XJ5#?Mx#d687A)KlH*E@h-QrgZadyzA8J^iZ+duv>2p8Od7D9(eY- ziA>$AhE%YxbON|bC<1suH6VD@{e8>)HEiRD-TZ91#Rhp&IFgm6OgPC75>3XfonlYz z*ZbweQ)+8PQ%JYbTidkO+iFt!92W`;voEE`l9c5Oq^_c_rg5ixNJ44cL0TRJaZ@sM z#Vsfm4Z=$T+0&T&i1n1*J{cH!N=YeLejvoNni_d$SW3CiSwm6FeQ#>LUaILFa)RSZ zy4zh)p}8|fQo$@&P*p9!Sbdh=9jjO{OQwOIEDcd(zcs4Ggh} zP98~~l0FnJrjANWRE7u0v~icNX{oC9rliywn!T#@xp}T`g5OJ4>*%Yaqp5P5-1Wk+ zm3|o&6twL+DH`?ay;8IUQlNr8DX=DESF!zHoQqbChr646(?l}NTpyW?<45;}8 z>{|RpvAF0MR+<3TIkE3z!c7WF3QY~(m#tr4RfWG#x>QmOpOhT<`VZ3tjkfc{DTpAJ z=b>#6H_|6$C~*QtT9DfK+m+%;z8e|q8VfFlnk6PWyEYl}el{kR?e734(hBcE~@POknNDK{C@aN4qHm6aieEW$P@W z5i^XeL0V0XGGgw0Z{%rb)6@4nDs=7A+{=|JKxxE)JIrhVl2wdkOnpXYjxE3YnJa5y zXM-Rpkg7L<4cX}3#XHZlt@K{c#7YKf2}uBwSPf4uZaa*p(~$i7pw%{-dRp3~IIh$s zAgm~=e)&6p5g7-3ar5WeH?!&6B7FJMaO!R%Kmye+Da%~w?MU|C>hEqFYDkokFez$C ztLIL7=Xyh`6-gy7Beh5&D+);jK@tXif%-<$4;&tT2%xPK0ZQJbnYDKvSX?(~=yK;M zr6t@{QhX@Nz-2@!W90>K@u=Si-cJlH8J<%kA1jOcc8S{wW-ZHejDM0|10)Py%=FQKWK;i+7d*Gj|7DPLGlK8aVrcGU4c24@bKg#6mazCq#}C1NGkj4A5}_{2nXi# zGmVK7K=JN;TDCsc{?3}0Na4G+fNX%&_op!#oPeYRWL$ZV->)cd*RD?2I<=}536+3n z0C@V$573zqIO$tIedQq_AwZWLLABJHcn+B~9xr63aU@h1a38;ZZE`Y#a``n}n&xW# zH&UiuZNO5Kkd=i4Cu2Dz0gT2tpGN#hn=Nv}f=c*wF5KH!pggmP!?2GU00krB)Qf#| z8pNJ*jZfRO%?NeEqQecK{N2hOsm#XN2L~by#@uu*(%OliBq<~)iuH2i=6o6Rjz7M8 zJKf^u$_b%nJpl7#=SWl9){Uw6Y0AJQILH8!b2~{Bu*L>9mg2Y(0 zi}D=0ULcpK>rGyU)rhW26xA}{KB9>r01WRLImRGw2W}`^Qv#3{z_EH)_pc}Nj>)ig znV6LPZopB)^A#oOcJL8}qPdenT+0c1pc4Tk6(pF%Op+rJG5Tgr4;zJ2IDkVE?s`|B z=}vLcuvnz4-H==#0CN}f<#P)k;;q*-A#E+SB!F;dXvp@+KSAJeT9}n3Ul1jpl?_cr z^{h{at!P&N$~)8Z}u!+E!An?4rIhvpV1d~?qy_hvcsSGhYKuSuO(IV!V zW_yi!)`8*ZepEG1s!D*=Dggl?kvRJkInD?lQ;P-zW)})-j)Zga`7~2@uGvcw#do1^ zhxIz1*#Pl$_Md8ojfY(c3T+7_pW+}0J9+XpKEsQS8b`U5{{Y<@-{-CAeB++t@kVmb z3)IwyH9Wq#cBNNsK}kjmoOKC8dxy-Z129RSdsT{_t>E#3KzM_XL-RNCuND^leksnIjye~~74JUN|(P{JvV zNYM8A{--GnJ}O#qAu1z+%z4mNhj7&SbT880v9hyaw=~^L5T|Lo6W*Tufry^zKc}<; zL__TSNW;!bRdAzDZ+Gx$YrJ?B&%>|X2T1^y6fObt?VTcFIem4dsb-x?T1YFnXm8A;>=*&DkOa9Fq18w0~DQilBcc^A&CY{{c-J#Oy=a2#*$S`I` zGk|04eg6PR;>#^A%Aj#a>6}p~o)Sh%G}Gbr>oszeFzYFmWT)xlKYgHY5hgfwC_fb* z*|xqf8q5Ht1uV>aTI?T_H8g14(;=3}Xqo;JV0&*EIf%!+dmWSlIGHzQ=dMkjT{^XJ zgZWQVZ=Q`98ptp99Y>@Bh-GKy*pA#z0pIjE`f$f=2HEq7KNyuVjbIW@Gv!OSLlS!_ zDj3O2;{3A{PEIP`owbg8d534aTsg0Oa;Yj)M&$b1Qi`M_@d_XTm;eoi(;LX+=1;Xu zYSFcEp-cF<)bEu~)2kcNqv%(&lu0`e3R1+GVd`l?=1oSvL3YzAMXuh`45eVEaV9%N zkp@(L;&UYC2b;E};<=`Q$){rHPo`CRM@8V`ATTKzYv_l6v4h(eBrK1cZ~zzsVXy}T zgX8_=@eZYx7qR`m!jYno&O@=d)7EjAUQ>XOaARVTw7>^D{#@=h&l;?|@YI(35zp1( zA_*=J<*?Q7l{D+hEx6i(B#zk_Gxt#jHpK6~a3h4!f|9N<;X%b2kJl&5tSF_4Wg~== za(ChBd-=XqjdRrUG6bkd0L*XnIWv(Yc;O(hr70mca5C>t)O55inS_cPWk>m2pX(au zTT^>TF**Hvya@BYFn;_tniQ4{K}(kvp?CQVo5mMHlBERLDc`6Bfv9uazWyK!`$*dYpotqX zjmP`KL-r%h;`#h8AN|I+a9j!7M~GUd9XYFdTUu5hVefImP)_3_L>i+;w0PIPTxrrpffB}+Eu*98+jy3pR)N!4I)*6)+7bWA> zpeY+n3?0m4f2aNr+lS+T00FCVscx^!INcKpNDoK#$-Z5qOIX)(4}ys#i2w;0{tygC zascv9N|NR>g+r!+>^v`)x^ge2PfAuAp{kb+YLfK&4#FhI;jf^oZTIa-%=^=IbT%T} zHXA#xw(CsX>Y7@rE^n}Dx?5F4QSLlSms9RRbVK4<3_V(~yMxCW_{Jb@W_TJQ9tajGVHORhsa< zLumKa`d&2DdcRNHT~STJkfpL-Y$?Q{Bm|cMLevtKj=)HQWB_6amct({N*qdLmt&P_ z%aCz*sD;J@5)YY#KJXw($~nN@X~E~bN4+3K2Tt+v-g zR@)Z_)V|U}UbEHQk~>KK;Y#H}J&L4zLPBTkMdD@O4PG$rIMr8gk>>H={{V+QuQy_Q zL)o$>5$-mjT4$w10sN#8kX^xY*$Oc+&gPuZR48Gu#apS=OaN5cOw3?Txt>T7yLVzvhEOj4Kqk%n5;brdD(TAmm4`UqLNSLuJ3+Ow!WUD3OuEfsWDOUrtu zwS-$K9%;nb97TN_bs_3?wkZwCG+wZE3Ta3xL!GHnR~K!_+xv5EZG1K!ZZd4yE~7Qw zAh2-c5wC|hM%1!!02NW8&dg0BROB0>YZdO|5Gm+3qIT z?Y5$(xu!i~MNWN1miD^og(w8hsp1DC2rKoXmnQVYurH>Om zo12I&J)&$CYV~TYcB+>Z*i_@RRIWay6Sv%517lwTLK@GDyV<_=877As#0L!79>zq`C&q|n-OiPkDu)AmYwigb{QhHaE# z_Q5G7Ex48v<7or|+7E64iO(A1cIMHWC>fZ=sAd5n+|sYo)v)CW%GujGQvU#zfR`;n z{vyf;Is!Qx>!U%Hx^y%Wl%ORew4xFRXp@i22@|-)PGb`fHx_XOCSr>zxRgmhI`}i^ zluDq4zY0PUzS6OaJ_q|OKMkJOR?n8J)8Wf^DQB=Mwl4tizb zmQ;)dC{$64p7yzZs6CGbJW!Xg|8Jp#K2s#-s3S?pMsN&|(Tgq#Vc051+h|kvwc>N+gn;LsFlMng(C# zez#~?;(;jwZVA~<{y~R1UH#ko${P1mwCV^cvJ0aJCR&SP<=IG< z4KGe~G;gQ3Z0G#spXc4jxflunuE+lX(h>4Mi2ne5f3Tmld)pfjKl3mA`-~6|^pn*8 z0Qym|Dm4lp>?iF5w=*Jr{%QXJ&bSz6lgr|NsDao0;r_yY&^u#eANiO5eZj&%x_n3e zv|y=D#bNoOKWaejKKS;7{{R{Oyc{XN3ZGsoct6$;jo*kioC*EBqt4H_@;~|a2MA;E zo>jkhr>1z>=6`?wN@L#B@BaXuf9Kp`tT+n~^q=b(_`keA*iYI#?fZY`-}(0$YSkx^ z;&b!*#sN{X!Ef{v_Va<;=M(Sqf9Kp~xhwsz{{Tt{Q@0R(I1~1P+4lbc&A;>RH5vq| zzvB`0Z#d7z{o($?e$nq~_x}LSzw_=gQlIXV-|j?U6&olP{{TThXdS!cXMfF~zs(3WzLLAO0eK(dTE|`5*lIj4?Mu{{XESb%FW78T=^%J?%dK0QuMceZWKA z2mQa5X$Qsq;r_yY(cwqf{{TPd`M3fGX(Ro0AFO5Oe}Dc;V0M9+pR|Mg%DB%8f4Y2s zSRQBh{{ZBsJ?%dK0QuMceZaz>3Guf|#y&6a5B3xGfd}9J06PBw&-lSedL*Cqi1L4& z9~bwB`w9C;oxa8oxS9U|&bY`*b3yD!&Hj;srGNogU@O1~_r1E?R&9O{(#(ToD0e2-~A?^JPs zEBRZW5&A&t{_y_*VLxaczCYs!>^r~DgNOYKyZmMSbb;0V;Q4$B`$wQajy3{}M`B60 zI0LzQ^Pqmi(T{lAGXsJM91uX@f(HZ;I3R(+1P%xwa7GCNrW!w5O@(rTjnx!^O|2}h zlA{26miyHtiJvtiZG@%G@_1HRDN};QpXK^QSpGExm{I^9Ntw>` zDV%!&?~WpP6hnaH<)ih5)TJ$)WlCSCuJLuMsaJ3#0KgHMlLXFo#=ukFEzA2|Of_#JIib$kH@i#VCRU13{g8+An%VMie$rbgKdn>9!{l zaA$1dU|66a9$zr+QC&G{8jzwuA=<1c{=V%20jcc^Ni%?zcL6?BXK39204?yENflDT z6(=SDXlvmZbrwoNr2wgIdVM0}m2?PKW7Tv>aWf66_QLUqAekgd5wL@daiXV7P++k6 zifS3oS^Z(hxtb7C5yTYEL1r%D#n0&(ExOW>OKvo@oJv+9BNMUz00@sW9@yhVq7^Ib$*dU%%MqAmZG8t(h8H?z61axX*r4eYDgrqo%=tYQKYa^ zzlp#Ay&M{NX8?8trD;eZ089W#m=J%NFma9*N?5lL1dHeeO?mpXI03-nEGv`z>Kd`@ zfgOQrQUM}T3Wj`;awF>_e0|gkLL7n#H)8zskb># zrdVm)!qW}7@ld6vQizqQNfM*mJ)s6dfSePD1)VrxK6+V;24%86Rmf=0-3o4vS1?u#A zdd{?k6||Em6DZd!WnAxGRO-;GY2Hlei$y=B`zh-3Q3`tE^NLcV^Fs~xG>}Oo32=e2 z#PKmBVd3SV0-iShqv2z5)Wa2Rd5R~DXOZzHmG4-`)ZC8Hx7%G!J2u*zLyj=Ekza0g z<`&9V`QZ)+S#S~oDOysJ2XAt6t`?oJ@GC&~iApF7-Mb2N()WU1)Wwy7C=v;!iC}tt zzGgN2s^sq}`C~y>QK79ir*9SSlXuh(IMvGWajI;TDOEQ$7Z!qo3V#nx=&;Z#Ky4rr zb(4m}Nd+>L6p^htHF|c1m57@-{{TJ0xsn??)}5Q=%JD^7f|U#;6p}qS?j`}1VLKhL zN7seOCAdIiY6Q0|y>bwN)CLCG+MN6&Ubb3j7hiNS2_a=_N*nA2M60<<2_4d8k_0Ct zMDW&N;c~tdfX?b2UiI+n3QH4a5Cf{hfY52LfDx{x)S7ys_b6#@bQO-al@&EdT}x8q zU8l4cTVcl*jF6cri6D0Zcy2Kg)}bvb3h3E~>z}5FD?*YAR)T;Ux3xiL2IJ~;jiEId z^x9v}B`GL`q!R-qZj-n%gC}vqr6f>MyS8wcno>wL;tUBr99o$Vm12YVu%YTSgpq*Xf)QO;|knZ%$t2n-1 zj4GMOhlRoNI_Ty#tE*82c9=gY1t4c8aRfm6k8TlXr4D`roM6sb4aIkA3bw2SSq>L_ zyJ00G{_1`A21mAM$c{B3AQzy`ysa5b$%-j;Hly_#_q<#}7DD6#dvHcUB={o}8G;AW zI7Lz%mLLPm?d#7>TtlU_tfZ(R zNm&4ZL5>;E!TZF}l9$Qt`?p?V9pI-TO8Dk?2VSD}0R5qz(y`PBDs1+(U362|nwHH) zHSBral+RI*q#EqA|O zyu(hT9kb_AN&t;R_p1S3@Ywmu&vr*M`F(Vxw`x^U={u?~I*FCo?(|huR`)3$MNp|P zP&D%iRzt$0v`4D0)7kR3l`3ge#mpbfEy72CzGCLB5)!dEVZfhw0u;dH7+4c`@V1Q_ z?N!y~WvL0c>P=2;R%cSX<(`J%UZn+>67%%*=yIphcCkrmr9R_Cl(Jo;X&@g}Nk0vY zT*2=6h$KB~D8%Fi>?kReNY0n7-+)FLtidbne*`S8J+Ex{1cA?39JZYI|xBZXTg&3o1gA0!*s*Zx6Ycsek}J zG_k&p{aZ>kOhtcb;R+@sDvA7$V*db350pQillocaPLNgmU-N?E+d8*W*3!JjowioF zUrlPaQ#KOAbxx@WWLK#RN-CJ6Q?$&uh4#Mk*0K}D;n2PignTJ?E?x7@+4=n*26inf zSt(E&IJ-Yi%sE5<0R4)}6cYs?fTT(=0&qg$V3JIG#DVObyg$Gaq}}Vwz_b3@!*9hU zWT8fx)~CC@+vygUZ)Hj)?kapYrB<^Bh0N!MP03nOk$5XDHfW4T!Y$p`CL2*V&y};&g zXu@*T9#3k1ZrL5vC+3oKn3J564&HO3J>Z5>*i ztYVG2I%=wvvbxEANfimEB`bBNlwC`G&VZ!0p~GeD^{AB~FgbVU)!Fsy4&?1AIDgEb zTEUBlzkB!whKI^duk@CLxN|d5YNo?CE0(T#rYKr)RHCZcLuIC?pt-bAuSw5Y=PH{D zNh%(KdxB*>%BKlBM+uaJG5-KQTsj{I9&~tmHm1(UaG_X~$sl}Gc4GPT7WzVa&)%r0 zIoUzV8?K1f6&h0W%bjwo{-Ue6wDKvJU~=1ANmOZ%auruvrrkqS_Udp|Sg)0CG=}{| zzK~Rsc;yu>VUh!im(QO{jH?T#b`DWd{8}a;f&DD>pbdJKXe#qd;y0%37FR4(SNAW* zsR*Q}x;ssAu~k)s1pfHisuJ-FO4Zxxq4yM%-3woF0rnSWV`rWsf)Y&}iVAB+9Lw41 z3^6-LWl3d~1t=54a?bV_t?B?G6U`;m-%z@rN>SMPWuQ5M%op#~^_7+}Xw#Q}!ctkQ zDvQ*KtiL{*Yc0Nk0;DCks_qOAO$kz>7ZN7{hmxRX%?K&FLzl4@A+N3Bgw3sooCud7 z@hO5#F&?ZlkqpeN%q8U+QeMl~mU+*fez|38S~v-07CK zYO4ECx9UF*^VIDv;LA?0(<&*cC^HYcs%({NEo{Ej$|I(f%yTf*k#Yq()0bFyedJ6M z5Jag!{$eL{`bHpRXFNDaOrh}_kyi2V>XnA`csKwCp_o?H)-hd$5#9dI37N(wdNSkyf=zJbdX9E*gcp`fTBnuV>7qeUh~3?933s*G+7~->-2?3kqJ#_ zIr&t`DJ~;l=@+|3R0TEalBr|@W;Jo)i-09|mRtPR@{aW-#@YY|eYLGz8 z${k9HkK-i#q;d1B+HM_5-u>a?Ne?zrO$(NMpDgl!FInzH*WHbZAM(g#*F=Cvs#Y2H zl|lWts{k1Npx`15fQOWzet?Dd6YUeU@kwV&`v87_w@AHAUlJvaAEsU5XHsR5f&dE2 zOy(nht_+W-zX_<6AdyyU^Dxx16bKIPz|ot=nRmTn6h1PxCodXPk;@1Q{5GZ42qj)U zN(xmbBw(2tJ3!da9*Xt@I7-_aOtlJRo~MNLt$F4nmU~hhdou*Vo8CRo^$(^`tU=`$ zFDjf$3rqzQk~;|m;uJ`nezSx6`o!YU#JC(yN3AJu);J-QNGSk^W+VDptY1F7qFO!| z_4+rLZ)$sG((Kls)^FU^P|Y<(eLHHar9n}oeU=DIuQpQRkhI8BS0+M7cNk2dx#^q~ zknZe7fM88(dB(Vjs#KvQlF1iqKDj&BU2h#;@-t9Bsq9qMZ@$~eDGedgq1fO;NAFNc zSIVd}lNlTFD28xH#2TgT$@@1$q(KxE%qeF%9Vz>E%RMf*lxYL`T|l;UcaZC#G- z=^-~{p%M7OfHFwLDJW8cfFx4XwHOwzV)rCZY}wPu_(3X3VjkLe_19C?Pg)%H=95}0 zOlb`ttOdT!qBPY$ptzeAdOshl1)h{#(@d2Br>N;G65y8-ePxc-fRvD`)*At{g4H#6 zl#&Ri2m}g~c6xH_&T&}0T(AeaJZx(3n}c6hcX(`kj@SJC)BOD8z4qgvHJ#4WY37E8 zs;#!t*{+opSB4R&s6Bc$HkxnWRaK;wC**I{f6-|p(rJXXU_`Byxqj^v(eE-GA>UT# zQZv&?V>=m-SXn7jK%pe-Pd`E0K9SZ`QzC|tRCcyh(i9s{A5~4%`Nyrf# zD=7$;0#Zi`XkRzy_J1g@P6|_n2O!eA-<`nbI~Yzm;WWAq@o=PYCZ3Uk>&y9wX|%~x ziU^V#Ocgq!2}&g38R6XJJd#mB0R&Q%(D>WV7)(UYBmgS>?Mhotl#VevsMJ?|U)6H; zFDGcNS9_+7wLfURKI*DJ4Rvnh+lYFJRZO<+FXEVGUWyW~)ghIHx~Nh>ucr})l8J*5 zppG|U3HYdeK9Oz1B4JFTL-6pD4VmiXQ%0_N3PvUSUUeORrD;`KA5t}4Tc_%*^{g(U zhN^;{53@qubqys=Jxn!6ajCbn_-4}LTBT-7>ZfnHt||Eanw(7aOhz%~@dDMYL*_E4 zVlyq?H+O%we#hD9kiDFR&P2WtvQu&o^pu-EZ(Ou*mX1R6-%;w6+^_64qBOJjMy_`Y zLa(d5)m5p0fGNZ?_7zw>JcOi7<4^z_+)=*1oO5mH z+qk{8h?y}GlP*zFDM$%4B$jXo)%ubl#NG4@>@cR%&{Dfnl2kGBgr;K=F)_H?bDaC+ zthh9sF^D%AwY#BIW(y?y^qcxSHoh&<)>WpP^l?EcTQ>WJ^ov@#FMw>`GkFE z+IaKVx3!{X3uUDwaHrn_UGpDPRXzF*#s0%KvqvnBj!ITfYewj;xt9HF$;gi4w$ebU&qeYLfi2WaJN~>iCpHV{p@zryhg#OJ;bPFZRE(nkVp~d0H1$6b|fHF*~o1vaM_PS z(c)U9x`kK(bfasSugjLX_X+L<=ckk+P&kxb-I&_X zmrni<1)GGZ5)KGG-*qGq4*1XY2it`MA(&;aGf~$<0Syf%9ujzidA-}j2u$zikX#}<2>P*b+YoKZ-U^OS1rsQ|=& zCY{1SC%DR{jWcvH7&Vi#G3%FC2s}z}_LW_1TSmWNm+fBxkCcf!!?9&!6cPfl0cn;? zijpOz@?)FEYV{tjNHJ?Qj--ytgk2eIHwO?s2AwcOhyg&s?(xEtc3$x=NyFkMBNHgl zh02scr!Y+oe6G-xjqBWDi%vTQgqwk$hh<8ih))3{{SqI-^{9f~b#AV^)P>w_bblwx z+m+80kA=q^@J(S>%&r_=OHSoiH!!P}V;?m^g3b_Wl} z@6JExF|!v-1SvPk!OET#F&3X_a*|YJYr$0T zF_gw;K4Zg#?7SP1!Kd{S`J3N2V)>YbnY{a0sHrpBrpQaNNRSlE+=fM($A=+V0Npx6#Y)lk%7Y`1c^b;WV^AUuT0$Y);4b0+B0SXyj;d1 zPLK*&#-W&2l!Aqmm>P$bcj_C(ioUsuDqEcm&aTx(aI2uExwS!1)kvnUZi(t~P{PX~ zdM>5r$SEbKJ3uw9=-i!|ys`=>NC?Cs3-?n-N@o-d3$Pri9Shm7we92fb&gETCNWd6 zQ>LI|@ako8OA2CLZrmvkN9~yX;L)G%Wbcnd?W3FqjG@N?D zr0=y>n(J-O?P{w2>)h6>HO|c7OAR?yT*6kx(E)5FOf=HH6W+HRC6bgqeD;0p(`e$c z2-+KVPvP+L$W*BcWfhX-f{SyyZj7Uhb%@pp|7HAC*1OJD>?7bDr-@;I>{H2PqQpvJ)*S z0oWv(QuhMhnZA5)2J&q^xjud`XkzD1Qn;1zD1?W1U_KLml!f-nOAIi;pWQUt3PJp& zB_Tt%=&t2J!I_-#Z^0z6aOEr+$Q8=ZZFIdOgY94D&6_raDMqI^e3|HP(^yq3Za!4g zXanYmFiC-!oC(1=`MaQZZN!HPR9}RK&x+SM#dzZ@q0s#(pL4Y zEdY!wA_fRi7!kTa@^Gn;c7{__c#kttqY4+!qb4H8(PzLUFgGj>m=ahTWtgKv{{Ug2 z2n~`TkN}wR0Pi@R#Lt=G1riyqUbp%l@jotFRL0EIdKb2I{bEgi8MdBtx}ouDVl=7{`6rUmi`$q z>zVC00I-^2k%{7G_;~QM8R=48yS|1xlH<;C^#qVdP+PF9d^7JB_icBJ&ADkUuHA93 zp>-++3q!QEZ&R%+1P;=bwxpEeG9XTRP86id4t^k%{HQ#lxi(tYF(mE z=k3*0@3OT+YVJLRz%mSDA9;=$V(_V)g(QGa#04lnPhBK1IH~w0rAb+2k;PVLYJw~J zL;mD1JgaPVG-*w5Z))1^9;pKae7Q5UjOJ!$IN^Ksxe~U7x$5^RQ7KV=5btqdc@1OG zn}xpr%#eSX&fIu2 z+7{sAHf}C-={RDPB}K>_>6W$RD$LRBjn&@CJ5vuSrlm_{yO1xSwQY0f4qZpIf(lg9 zz%8i+6smFvP=U9d!H_uOvv6~z%utw;mZuRH}q)NEiehj7TGHHyC1)N=bJIPZMLUJJvRqppc?QjP*I?S3b27qpG{K zl#ohDQV!ju0pbQoG3U-F#}ZPo>ptv<4W-NKUt0KU39Ud20Pe$H?PyhbXQ-R0ZOd?2 z`Lo(pI3flyv>3#GlgDV>JYNLKa!EW&AYJv!qSnmyEO{pUV7SS|BoG6V+0pDRO4Mh{ z4L6D!n|)fJZ4W3YN>qfBo%he{u>=`{cH_`k2?~~ipm?}|U@Ts@=>6I_eDf`9n3t$N zRTs6Mp|a^4OHFmnZ`1mVaVP~c1p9+00A^x5M)?HVc5S-W|s;3S2FP5(q+6t>m4I@9r`u=p&2v&rwiI{!&;cA#cBPSmygw0mKD9 zAx^AKb9&RRu<9*Vl)l0UNImET4D3Mn9x^}VadW~433^+#EAnoStWt5Sbsl#auy)eo z3%ajJA^I2F4JAcLRzgXc1gi%z18kE%3=t+JDM_FNDx?<6J$_9LCE^n@TTJi<(p-0H^W^- z+{pnFd=6bmmkt4EFCVa6(#9BFh5Qi+B#GSM7bAFaX2BpG?YM5R*U_q^^VM%Ro*u-v?0sPEXa6JV8q^DQD)4p%k_- z*Wm~YE!24=-5VRa7O;2r&|`GcV&^rH3r$4Cqm)RbSGtSE;{?2z3nU7wF(D%oue54c ze>=psJbRqwVKzs%Fv6W0&i66RSw2dO@zbe1WMO)<)Y5&Tm$FQ08j|>uL%!QX&*omP z4DBWYDl?y9OgB2ez+qc_mYH1lqY^yc#k_|%rbxH=gOfNl)y$O>+(f7GbG4XEK?)o> z6)0xXAk%z;pTG>GTs{|tEvRw4WL+1lL3w;ejW!vMfK)O%;{2z}^Ic=wVgvWLE@748 zyk*{=SLp6@iNY0f^c|I|Guw)|6{`yosNo6zA3&L_M9V9FIfacG@*cCvV*5b<>lKXO zFV5wOU*9{^7aF+&P4G^vOqA&*OE4EVW|g<5anlA6DkC(q9ev*;LTc4^RnQ?ZEx2*O zFR&5ibr|TsZk#;&&geK!`ek+bFy^O^Wmuw8^g8`pz1M*uNWHZ|gF|C{h-pjfG0ULZ z{lMq=iJy$Yc2mH%s04&t%gD}Xr=ch>De~sU0){&vzmx)Q?Pf)~1?5P}u z6XhQzAN2{VIn>X{_8?!K%>{3-&bGDA=?iht8H$(2MBQ8iRtFMn&n#Dt?K1W4Nm1++ zFt8NpCB70Q)6qp)_a)zrhpYQ|z9j!P^VBK~Kw~wnG_+E6o*20D97(P55Y~$mb+=BWc zitrimoHfVa(BOSgFAGgY^bbXKLysGt{d~}Rxs_2#XJ*VKi23K})rfdlJ{=5L52G?s z`Q2Gs>qJf4Mi9HxE^NgxLitv#i^+)p0Qs7KZs{LXFMr&oKv%`fX1m7gY1bGYMI4TY zfrr~2EG?Mh>;ciFFz>QGVOu&f1&*kLaZaAEuem1^-aB|oJ5l58kKZAO6Qcouzo20>oqJIze- ztdw24q!&-4#o@5wj?!_I8oi7&b}zy1!N&RZ780oa^H`n5owKo8O-X=?By#f0n90wP z%}zZR!Xs)S3~F=9uLrc^8ua42TQX1o5DodvUz=Ma)cz?#q7)5RTZ^~0NWDMT!m$)9 z)d9(Q+hA_k=iMrRFhPH&8$HvX5BvAoTdDj^b=O!ZV1ZAXHvWa6PR_`fv-LkrqNwefkG$Q8U@t^ z;YnNKNh0HcX)WG}WZ^n5WA|c;-ZEJkAbe7nO4kH9^8g|L4PWDS2Fz_cxlN3Z?GJhNn zi+~@F-hGR)!UUtx64PV%Fh%E#+ko~}Q+nJRa3NoV&a>r!uy!|xD)7_gEMqC*MC?G+ zcu5i(H3EqM3H95895I1^yQbc!H;U=6C$ih%sbZ$0Gd$u>30%+cz{3ecXfZIT{Yel6 zQd*;54NWUeOTG;B%*Lb<5`CsBF=Y=+!`t|~u}7F~FPu!c#B@e6!j+#|p*o(QA(J8o z0PLXo>L$cdp(9ryT_7qO^mzWWg*aZODr>^2e%0akNmhZ6_6|!wTDBuqUq3zP&*}%w ztzjN9oob!%O~3&~nPZ;9H$;e~8tYV^`>GX3rCOhUY~vZ~s*AO+Vmp&MpKiI>-kC%J zlJkASDgsoY*VN+5?6e1;;iuo=DmSOf#W6}Z;0zevPcL}cgdAEVz4Lye&4FBHN%vnT zi_zuj`R>!|Yoi^-r6W?$h1?I-aUCt8xl=kbo>ksj`g#>k%B6DYnJPb8#+*tOI$3PX z^!|s#u$2xEJ!ot(9CxeX8^{ZP<8ns+! zm;E*@t2}5lM`hPz)|C3s(NgpFT9{*20{2JG+!q-!PasGIjY1}fc*MLuJ>&Q~t~Zmk z<(t+c8JrN!fig4NFo?P}t6`kUsjdpIN_k168+5Ojch9qZi1@ds7uJjCn&v5V&E9ET zbGCy*`UdB=?%=RU->q@PzC!xjnMRK6X^kfAq{f&4h)2hUU1SZKLww`YXG0%ko84Qi z#h+=H1WvQhU!F5ZxUwKK>H`|XF6;=4LNX+?D^B4MzS}jgRbv-u8;ntinCb+26$55Z zEdrKPqk&mYlPfRw|DYOwdXrLzu|AclHEP-K45y$!rZhEb%r|o=4zhvs&#a>VQm=vl zyXseb7NcCtEluCC>E^~Mw-rz=16@9Hk(-C`&JVot7W1I12}*z|fOUzvhtR@O&2xuzZi$N}JEmS9naK!mhS0RYsV` z(HN-LKYh)1Y1~dQ8OSt(P)QWjY?Pt(Qsu#2Rnu~CmZs?hEh@Dyt5^OM*q5BNxUe_@ z9<5AIKCCvd0%3#~Gjpz=lD7XFfc98lJ{hJR0|RhQLKt)gFdhneJFQBXlR z@A21wUG2w{VwrhYIw6sPvWh>6XzRO{-3bUiEfJGFpn*k#nZBuPknvp2=aQrpi_wu9C?OE8i|D4jVz$!T%&=6qiGwFRpKe zozm_zE>yO4@GGaFjM_h~f%RmI{Lo8`?$)mNpp6tx!jiJd_Gm{ ztX09gsi-XLx4hwYWgRY*`M2g}wvoBb&6SIJ4vSswS_ry0EmZ zwp1NDU&FMouV}_>Q1Q8)m1)l}p46jl(@s$h3sfbt*p=<9va~$Q06_{PWm@|?wV{%9 z_S1R=#h|ovhR<=H$|va}F$?&tU5$6M2%|r0;flh5Mk6Oe(N~6BURDTBK%1KLTu#tb zIQV&yGT7&*QXVlY!G*QtgRnt*+U!(si$)rgXh9}*vXWEMzQuEhz>bTKT7C4nfgM!U z!EKTV(ia*;Tixtg`0mo}<4B2Z3QeCi7!6tFrSV!^^^NJ33ajnf=u`L{Q+~ZTL`0wl z?3Ka)9GQbWGgB^_&&Qv#t0X7!8j9K<-lGSgW7S`ki0pQrFfit_p{}cD4W8cH)2*H^ zXLwg+u5f}MSG+fAIr5k@g`N^+9{7ijE@T_90r3t>`pO~RSFG;*f$xHJxrnW{C3;Mf zhUpaW5iLXf`*F*5(5@qapj`63l&q0iPP`4R*DT|&EcB1&QG$5i=_*TQ>lA9YN3&({ zmk>GLF42=J2$>0UDM@);$UV6D{x(iLH-=xGG10J;SS8(i+$nGxW*(cIKF`b#e|4*LHukM;llp|syow(Q%XDo9qxHo;ZS za;m9xWMC0j3lmh8>LEK$}s);yY9Lk zjYNXp%SS8?i*`OZvZfFjGc>_(4w7z3ex4E*suerh%29>C=jS%ChaFx2lufXBb%Q62?A%9%`?74oqmP4>x^S}4V`yYeY~ z^e?6posWJo+Jxd~DtI;>5|NYT8=XwE9_u(nO{L>8AWJMdai8);yr_Vn$zY>b%RDdmV z9HT3RCvipxK_iK!}3mQA`g^ha0 zUJP(Ws!Eqv^jbJ(jyyH-6f zkruPVdPUWD_SDa9DwAl;o29RhO*x_c8JKt{2ch~6Fz7zJ&1GwLu%<61z7&>;&Y zCjXPPSW0HZYtrsi)v>f6CtBHsrCRfe)H)n;)~?oUIBI|RhAjIn`>T}jn+K!3splYK z5(B|oa6O2Ck?FHT{LQr;x{6`QmL;76t6tpAOS^}mpheLuy_3WG@DW_LS%LAc@YKYk zxuqWj6y=*KKRTiU-2S?j>O@8Og=?iSz{aQsN#QUg2t-pv6+`|Z%kD+Y*of`7a`GZ1 zeLZVfdbEA+Szf#NV-vP?qPffDrAUMYC%UQPpgEeEAQB>2VNn1Z2goTN`mhF!j_Y7@ zzgy%OW^lMd#m|oG3n5@dtlPw?pZJ!9v#9#_r~^}9-~0SKS;}V8eReJw%hfVqtq0;y z5Z@D)U$>N;6q+11FO!n};ZO&gE#n;jia`~c_AjCsP=KVK)pdRyPQLP8R`n0L(|jrI zfs+YyH0>VpzQ%4Q=wJ$0Ez>Q=M>>t{_v2!3C;1jpXr(NqJw`fWWZh5DG_QJFrZE$IKN2h} z#6|9rMPCqb_Hx<-nvrk2co(y8N}SMarx{E>O1dn?JWmAVxZ=kSCMU5`3&r(z9lB;jk5f{Gl}g|s z&#egOoZYpW)2dI)vpT@OGu7&+sBOTBj@6EOs^^W~%@kGt({480s(h!eE|NaItWADq zOpeJR6@o}=YS{q&&K=fHL*EMW6Z?Q;5x882`0d-Gy1v!XLZoXQ?`bO-DpYjLZivNR zmX}ctXRU@M?i>+IKq7!YlEH7v%VlARHYQ|Al>;|2P0&U~udj_9SYAsfxtBk2e7*QX z$ekgJbSEc`SM$zEHNZM1L2b1L&i73;P_!&5@{i7ovQ=0GQAR8UBSg8$Rn(@QahDtrxx`i_Mf{}FpvMn+=NQhtAK6ha}RL*xJZ`f*{YKl~z@3wuW; zKX_X9yIOzsTjXa>EbaXd6;NQY85u^uRpOCl$Z7Vyh!eeAObmUHy8J$A{o+)O?lTz{ zv%dc?8SiisGGbH=?#6!znXx!PYErz5=xV6ezY5-dgxfQ;!}mE&P22u6gr?r+)FG>d0T zd^&hAq(T&(%@hEM^-lw)QZ1N>UMUP=5kzKvQTA|lgZ%ILB3}oM`CO0oU)V-D7G+kH zOJUBKotid#DO~xEDt%y1dbD?CW_lVi`57VJfhl*pW zzMtp5E`z^rve3ztC`6;8XMDcr`LGS9)Y>1oI-l#S7+}Of7_4qb*8hHrl06hNqo4gR zc-@GlynY0}@UeS2doBGf>zQbR&WYPVB0F3ZTwLwcN-o80_rfp}UO_kTH5ZlYiwrf# zO!(pai&r@EbCl`l3!u|R#SHgP>uFeW#9?? zB?Eft+6`MrFDfz&+i4zO59W*dQ=_GRAJ(_^MnNUwe>0F|a`{I``cvc68=vIx22Jo>-S)cN zkBMXK({w*F>~W!PzHp!qfG1T{F__i7)lgd>gLZO{F*6H52oK@$d9)ClS%_>$E+iz` z6ihraE+HIyKY)(MKfM+Dv*Z#kV())Fiaf!j_PS?3f4a_W#BJM$hmrh|maO+n#*=@b zKu;g{1>ImzYhBOF0ztAep`=h-&_#TQrGWU-J&1{ElKQ1!*@99t z1?WVKgiW$RLEYC8)r^@B)d96n_s2Q144M?!>!&B>8kc+nYmXE1;IPHNkyJNul-7R( zX_|jbYCTb}AXjD>T3ttx>~U?OOmXHEPGtn?(KGWG+?KkYJilLm6mb(#mk|9*FU3z( zS6pkp(M@=voyC0%9G)Y5R2d#S9*iEQ3NSqHXqtDQ)wfW2eFtOf^{%Wa)H1jU{^8i{ zR8U@3U2#U&^367I!II9u(ph_8W`u|V-u=8x%W=F+P)6vYl#Vo4lQ_Spwjq!{Df28M zNHlsZ+q6^5b5ozSI-tP&olUk{&+pi(v`t9rh;&w8f3wv|JKKR7!#?e^hUV&;B2!RN zL#3DdGsixsJ<#W4L6c<|XXZr>X0kywqTQc(SmeC4o%x1RKcUAQ z45q2ft1K%iHOh3zbeJV&U=_i%jBZty;lw1ZT!^7)3x+@mkit;OVlQesqAT6lim6JU z`UdTZz!XSUrOf7!6B=g0kta?p)V0ibMJ2zecDdV0Rzz6N+BEaXuwTSA_H^eaSX_$q zW3S=PJR&<74jr8!rgveX$(k7F3|u*xfP89fa*H^nwDp4p=RltxO_Yr!P(W3XGU=NR zqLOZrg@TD*b0uhU|9Iy@=^&5e;AmMR`cav=1!)HPSms} zM2weFI76bg@e!QDL{1Y%v&Fp;aFwg}tbO4TqKx;*4XQj?Pj+oNmi?(1v!?DlUwy@lKi!p^L6S%vo`AUl@OD8KLSw+OLaoH_7)yr_e?-N$!hBLpc*Jx80Jk=K9RodGVu;CQF7;5#e zRZ=G3y2CVL)|R-E3(q-_3l&hth?)$w63{VenhEV?g6yjOBc7^-p$1vQ2rMC7u--3R z85p45eGyf1QAua$+%NvfDt>8|%AqfB_7As#L{El^SHCQ&!oyFqj+LMiSl$dF28sk8 zm_%%oL9~%N(MD?w`h^_j3l7#1u6}DFdK#i(l7z#4kNUaqhgAin<233iHBx zRcCTIa^0H*P4YqM%j=Zr@6`-sd5YFQWsC~TopLdX7!rV+dz@VG{>_JY&r?M8+>m}u zmggP8)`6Qv^((wT9=NJC=}XGm8E_$VCq-T!Rz~EJGpPTHj$auy=p<#^=i0APQ4jiZ z6l%X_`{?tslY|YbbQ@jxBJV!HTQ1IMw0!s-VhEj$|J%iRKZH2JX4k9^*HhV!{OnH@ z$CZ^%gn!l2Pyo1CMJ0$GKt$xkL@KO{RdBMuvsKUycyk1@769FYjHvE*u@!<99?w6C@YT;c~9vs^IsI4yddqISoK*(G}nS0TeGV(~G4WJKfD=zGh~L4b20q9GCNkE45G8 zik7ombo7;*8e@b8?#9Gnc2&$;a!vC7sbh|Gpo$Cn8uK^fAwm56QeEJNA2+3r?TqnA zmSvSKCMGh+rZ|y)mQk00Q=r=M8TiNvH+aTyrQ?r*^83PaxtPEO(wdr<4a1h0Bd))0 z@8>n*I!%;X)bd7;L4q8Bo((xJF|M#W4k~WlEPtgX>7MU%5D!`ZtXxWy2+bGEt5R7IrxpMGQGh;aP zM?7i+gt#&^bl7&@(;3MCw`oyR+FwuB^=Dsnn{VlfeAO?_h1A@P}w; zkBtimRHKj_^j#I`a*yU)d>j;tnR!kBy?mRXYQN7eWc_dD=(@`A{h0FYif4zK&~VjQ zS>Aupdv`KJ>_u$$9EuR`&(`H6X9Aws{w$4Xf+1fIA##9VlJ-)n=JJ+6QE)KCsbsa zzO+OkIf;|HLby5#%TTTT!5gnY`?9D61+{XG2?>EoTp`rbr}rX-U??iRAp|iM+)kZP zI6F@(yIsnUsmqN>E_|PJobo)XMPA-U2cZuwb)}|U`jzi1_M735a%@y8*}m8y6!+Vg z@A6`Jbf!sas8VU2i}ayHS7|z8!;0EfX`3V0z5l>ZNx4%i!i?6)hTs26Kk&y|0*j>7 zFobeNx}YC$;$XRQISW}Ao`jz@K3w0F7L}Gz6Y+agg(0+uma3qG#puxxZ#U~iD95+d z*{x^8lFtM+>fsKmAF!HL45L{CEH4a8amGm>Fbj<4nJo6##l zA}5${fOZJt8_xa%lH`?xyb;s3IxpT_E63++Zv`F2D6HKayVTwf>9oX@E< z&i+vO((Dt7D5`_iw(Rjd_Nnv_-?@^T(){Xjv^@<*Kz&4wJk_5QKxZl|OGXb1n8&NV zU`7BX;E7z&Mk*$!A)cHJkOt*D7PipHGD*73720bIM&H;jw}rgdXk&s3r!oZOP0QpU zO2$RDjg^c+4@SwEMKtJ2KuAzcREZ-r_J7NER1x_cgbnE(X@Cukk>i_DdWQmM<)6RK z(jS6L91ef0`gZ9Y$6J~JR2?=F_R~A(a(e9ZYNR8K)ueYDIVeU+-zdGGffyj5|YMk&8S2}^GZ?;27Sv4%hDB|nI71a zih#tVETc2gQHNRp4ti5_qVs>cgIfEc_HL|t)#{S+FFQTVdN}V%z6sjAlPe!7>RbCW zv<*l?MVpg}B52tU$}T{qeGDJgfc%;5%M|9%qvvFTv&yn1A$-gh4Xg>{3FaUSP6ai%3{S1 zbR(C6(ynM$!p-?hKeoCYRz4n|wnD^aq_68+66KEBDhl3wjTp$kE~02Zi5El^wg&^& zjnmvq5w%v0}X6T;(r8y#zSQWY{IacQU;)#o`j1F zhK0{fjTfx#W^;FA-RXGVr4mpLbTIQWhukI}AAYF98Ex;523N%v>Hf9-;W5|^QTLlb zAyvUaKrzQ`Tip^w0ohsV%cEnBl8<0YPD*&~s4a@K$K~Py+MX%l!yLh!Rq0fiRim@# zAgC08CxJ(c1~4TV@W&<2ZacNVM9=M}>DhdJ4lS4cS{`hxEN&Y5^9M*M zx^?cu%BrL$qGi-q&bBD=>49G&0&m<%o_eAD*1F!B!AW+3o)g}`yWU;vWcw9mt`Ltz zLPdKpmP`my60!`ln3^R*O83gY->l3gzpmb33xRxw+X&l;A$U_#dr{?QJP~6RHMoz} zE~S>0_8I!&Z?z} z;&a+!`~~WFlfyoW%Kilw@oyjra=8WVCIr`wRH=4KY)K#p(uL#FsI0M1VPQiLwCvS& zK^=3N0nRF6FFWkNAry6=QGu{Zz06RYBe5Atfw#_v!H@ZHqHuoqc(Ys60B~Y+)lp zt0GJ-fmDVR#mMu6lH++PbNHoyh_Qxj22s8Mdg21o*_Q~Hv)S-{*B2AV*wgJzI7(M$`Z5Dn>_|-}qsF@G#L3ws+xHh3_q0i=}%_IM=#RYqVDK3eQb=HEF^+sv=lD_%+2MvVB{!+h9AB9OZ}V z>LXocr+`>W0>I8X8|1Bp&V-4HHXNxB>}x#P*y8uvpIFJZ@j=&VOJax6u|#e$XR?mb zMn%%J)yxCBusz6rl7nC5Gx5)xsQ9o=`VXmy#iLX~=W7C03VE91z6b8e^G z1tV3vs+q-idahl6t5;})dym+t+#5}t_9U^4h(>cEPVl$5dp=i!hRZU#0EyBO`#*-_ z)=x6Xdn8zl)c;akxI~Q(ZEdPJsL69oqmYrfxER(Ysg&48u@ zBl#;)Kh3>^$*ImJqa()~w%T%ZFm;qb-e!PYAMV_j6$V6x51}eF%*cp@@4l`b9)=g0 zAjRiKW`u+%4sTT=Wpo-05FPGCN=oH0&0N1-^FBG@X@(bN9Dp@Mn z_|p4UQ6$w6e?|=flFlrtkOs)k+qiE%9&;JRa|o(*RHHT7(irTMdnH~ zNONM69nIG;fynd}Dh}v}E54FU1$?Fv24KCTrR2y?jJcQPE*qG-jczCsa=b3YPC%IK z6B9ab#b->|=%3y!M~sOz3B>rq&^9S2Gqkfe#Mz2}*w4gbo+PP{Fs*Z30(lx}ppU2M zGJ#p~s4s&;_^7{pbW@Sw-aW7#j_2WOQ}*O}Fj?_BD>)e<+_o6Pvr_Dag0W^ z7=Oxry=r)LsgTrvB?b9dxvb|m&Z$1mJs2!bvVQD(2hi-^K>9_dI*T77hn7Tc=k$DZm3Ot@4%6pFoX1C&3qcm(3&B!TFT?B}Q;o?|%{aUdLC&5gtIPYYO{I_TO*DQtj+LvA?oxAfrg! zqEXK7pfMbQf}2xYpil|c6AZ5hzl9ERDWeqQqSwwh;PBHbVcykl)J5we!O!XF;b>nn zHV7Dj?$3v(_@KJ!887PmsAneS@E7R2=jQpS!ns&9asZX;By9(HXb3wh^WuDP*!$mT z?~Wy-NqqUyQtK=?5!)8}z~(;n2Xf{vm7hWaE2=c`v=Sg#;Q@>%mO+hM z@t7y7(5O^-?`tR%q=sRZ?LL1~Y(uHPVxJ#2pmL`IP_KjYP0^q9t9O;m%z<@X-iP{c zqN6!l_}mURiVc5rrosX-a+s%^zx;R_6vmGqs9+jlzu}{)K1y(z?)a;yCCtvFj_40< zqz;WHCL^A-#Qp07+uQ>{H&UCkMTz0uf3NG*6_>eF3#!yeDSdz11i@fJwc;v?%aYVi zE1aT*YN(vV8xL{!?VDB~Lqg_wD@R{D3aJ(6u__T4!WR<>Fa<*xylQ^T#{Is`J^qi4UXo8(P`2ba~^jwIk>dX zFR~v!m`w=xGfBtJEEqCly}9ubFmDXiLFmYi607KUW4`E0>x6h`H-fFpp*MTFt>&B> z>ivA`)ahP)%^Skkb!B9y<@$@$BH?~#Vbq~C5Tv~jg0CcTL%T1ar9+rpMU*7wvSCULiVPwHuPu#<1E?7)TsLyL?1??R_OYp-CO=>wd5ctP z>|EkSnfUejb!@2hRaXI<(3QF#4$aGTf1rTBqm%;MF?9o72r{6tp@aY; zToqUPSu?lmqn9oLiUIwa%f_yR^?(S8YAQ=F!U?l0Q?!8t{L#3UhyHm;>Ely_0ZDpYY<;l zU%b36!M11-M~>x%W{>mp^#lMh6^PDndiHq&UY--Nw2sYe#m0i*oW4~Go-L%n9o2iF z82eVNqM>D&=W^Tf8(mMd1PrpFq`fbrCD|!CiAy>AQ&$)+fmAiyNbe?$ouR)V8kUK>zLzHh!y^25F( zCszdi@)?T)2vx&NtpET3=PlxsstHFvy3P8l=cPg`$0UFYN}7pFKR;V`hfcq$n)6m} zMiUPtWG@K-l`Rk;n7i*2vW}eDDUKIbl5E8`ER*xx2?gC6?v4#X7?`3geoHMcTw6y8 z?-PliOnmk)bsQt__XIecb^=M95CTe!dbsxD; zp71hP+P}I8#KIOLmjCK^>-aDT8yhI0003WL8JVCA3_vfJ<>YXXAo?QKT2{Ca}wnr4hC$7TS)}$6s4z=QHZ;0lp`gkMn3@O z2cWWCg7@No2UmwkKcxZE=b9uwV#e-T2WKFOt@30?x9_Z%DiWFk3oYu3 zYO9Sqa;{tb;R+ITgWjNI98+)(xw$L7jDH?!F-{`XzQ81m+tcrPC0(5+B3ZLQ6A5!~ z;%4S+ZS%bP+8JAYJCm!F0!2AKJ*^|a^+sSL^73EI7Gc10EXCP!*AIngY?*fE)YK5f zI5M5fV8lE8X6=%Csbs}bISaUD?i~P$*7fGeS3$rU^bb?LhO52n`AJxFYY)}sQ+E1^ zi3X|=o220po^WdSre*YM>~3KB&EzV`#a=%zN&#hXrG4?7*q9QmP~aF>n-X~KQ)rS< zsbAQNt|tbx zQ;EFEWC_%1K$bkEb^mg(VKxx^gLy{k`=YjxI-I;dm!^e*5;zD#-GBAk_m|9DDNIZw zYLoc|4UP16lX!?hEHEV*nUtT677@c&%%{QDv{p_}Ey4L%R9ec~BRUx#pXL+m{0E#Q zYN&j+R|x@4BbTIF>ppx*WE^Sal=<<3V zvgR6EG#g)6c+*xwRC%DlqEpWCI@AJN6^SmkE-V{K;}A;4V#82#?U&Y%y3%G?;X{;T zzz5Y67t;qU$tt9QN;2IEL!nBW5JdqHqO<`1lsvt7v-*Y5t~i^=2D8TpqZek*lItqx zN*LUIx2(Nwp}_c4tEjBLakU?uIUqZ2xzUNJgpkX?GN86RLGQD86UEf}TD7>tX#nd| z#<-Lfor4?=v{xz9E?9y!H=gPvsY(rpNJVj% zF`K{Au?knpJmA}E2LDC7f0KTnK6*s|2k0i#57w<45wo`3JrL2J`ge7*TagkvAo}T> z)|*?n#&4u(TKt=;eP1yJYw(Tl=l=kC{{fVi0rzi#!3i!7-ka4;? zWZn`-Y2XDZH&pQnKLFyHJ81Vlw{GY6Oq>4!u9{xmXf>Yo{k1f@`SgOH!U~nbb)ME) zs1QDVi;<){KApisew$tL2fb}cAnuNw#gKd``Y>tvy~nLfnkzOAbyCt`GScE(u=r2# zZ7 zois^&Znikl`5_HWl5bM^=m`fYgQVD8vws}-lMXSMWKLJxsA*cY!t} zGeEGzV?|22V`gLO%^>y*{qj9z+jD;qy1RsACGZhMq82vIX%V;3%zot86Yc#6zg(|}gX)3C zvm}s%CGr(Hk+VlU@;`u0adZRsSnCUq)AwgQCkLONSC*H`HUt`5OQK z01`HX4dgecGbDKO=I!rlegXLj^ufZue-IkBKHZ5t39$wvh~7Nw=Zk`pwh;a|f~(+= zg2>B2;Z^3gpwE4Ni0WW1#afrIM=0VKACn0fpM%vY(G< ziwn#%Jc;}VAcXO(pdT9)!8Bk;8_u~Df=8(ur3XfzI{umw(bTIi#^k!1Q5?J4>(SXW zM4D{dcmM_DcO*W)3NKR~_3UvWtQ zfSTwcxY)bmgEG3wj~ zb+cAQAFq^rS!sRc$h8m7_p}?{xZc!h_+&FGe$F&-c%OXGm>g`o__822AAJAZfYauy z=Wr4DvG^r_;~VGyHc5HJ05=AGm1%k3S{CZ_Gy=gq+zXVG*M!iV2QfO;$r+CsuHbK< z?E7Eenk12L-H^Ih^|uWk>*B(SK6r7XB)35RBX8oDyJy23i~X~^4Ux~UG@|+Q|J<|k zuY0dvl?CPc@8P#KSg%6rd-ry}!S$*&n&Qnj7Zwny;KkVzQxM4;V3c$^pTXTACpqXz zo|NTn@i(FOOv7b#s_8+yfgW)Q7Y?U-a&3pyRETHr0W5BIFmDk~ZhD^y&+bdHJW~UTk^VOW@xw!zvZ=^)S-Y1@yRnh# zg4)-K`~d?)`*?k=E7jWW^m9KA8F!oSSybH*tA)#<^%!P92f|2z4Z(^^%@W zYIV6ZOO|Z5>;WMcHfsr1oz)!%(od?EW@V!T|57j5y@{=8z~Z=_%08MB_sw-lsv8NP zJD*OG%&SdLpDo+}pb+LEE2M`W>Si$$s_p~yr$&X%c8Z6A|1UM8|DO&uMmtO(jc*$W zV`G+@hK1AEsxd(|(-70>6zG^0-pn8$aZxs-JP!T?&>uY*)EsGnrz%T%-x+BIxglXxS?*V@k>w{fzNt2$%=QW7Gx&*}_neL0>1OZA*@05PXFwKdggKQQ zZe533FLSoe`uV)aBV^Xrq5Xzy#T}4L!fI>lpi9gN;)j|*&8TJkN;(fji$lYcq#PUx)1I( z!yeO2zLpzko(f!?ojR9ZBZm?;Bn$iG_ME-azE`X48Iz=rL~awK{ITuoudP(2pwq?c z#)zgfTO;s9CHEs9&iDOOhp*7!474?+V)cgc=zTJw)G2JRir)ZJ^w7RQB(txxm%y*r z`4$a31RhWKC9ig_HFAcU)4+tVxG%8|hy-UE$*k3otPvukE1EEES@F1unE%{rz!ujM zL&jTM=HwNFDN}yuIIBLx6ESQpUzV8qV!017#(_#l0CYLZ#cafdptL$M%%HI3qDc(i zK;b`Dwtu?7^FnF5t%T?pY<%$7EqF+QznT(wM3_h>2RZ&dPz|hRGanKZkifCJD%WQQ z1w(byE&QyXZ{nP^4j&_oEL%3}!r}+%sF6Uqy{qKDWGZ|f1rSCA03KGWzHz!6%R0$8 zfT@Qr%0kD~K^5PG7&K?{S!o{A-O+b{2DjAYKFfNE&ZtyvBofcyG_)ugJ10ei4+?Q! zi{k9C#*<~M3+%ssI7WKbAYaNe@p%4Qn|3|iZU=th!!gGCe{cM zDUW5r2eG>10eGw1MvhlTYe}o$@g$eT4zmQR_*tQU<`oKQZPZnX<&Po z9u5U2NWv2FkQf1ciK*o<2s(1Im?=@eq496cGTlb?-^ zQ1_iz2jN>~D6rh~W^OLA;jSdC(9*A0!Id3#4lx81nQ|IgNvE-yVM;2sNwN8x`eRq+ zmc7hs7zT)1%^=MH-lbQla#3{_P<=2gpjB~(JO79BM!mQqPgdGftE?q2rtxz`dg#>V zY3(a}`j?*VRsGaU4HS%zop8D$uMcmONh#~lpFW*%1wxT(zrkYbcBxfYP%F9{64uK~ zDd`kiiE;ahNNwB3>GII5TN(Vwi|P1ua<&xbaXwW+U)qz=rJ%{ETn%9!kSGbLh}u^4 zZcO2m{~F;AVhG7E;k=2BM8OFwA#K_^Qgco;bn8W z-nVgx)w*UxOaU#U=#c(-!og*)Jjbec!bj4h!sx$#jIA4f>PvZs=c;}%y`DWB{myN< ztp-tGR-A+DXMKPAdma96(%ThgmsFDY!0&3em_eD^4WH%Y`fIEEej6IGG{8Me*R(fs06KYVWM%@ zfC3HXRknGlZ(0F#T{R2?0`aVGU!IEx@SkRfUA>5Iuf!)APE!fMwTTI2XLxVi287P`9Ca40BAcH!E z6G?2~VGEzN4LP3r#)Vywg2rQtPI9>4x;maRG7v8Omb3k$(8IHSP;<0&xfC+EWw6*vXsd5EpqRf$#& z7OUj?T%if*pR|KM&)JL}lP);duIfL_0{h3AM1Ogr8hCO8mof#DK1zx^qkth2I!MPF zzn6=9_?e|bzC&YjB`cP9A9;)+(`J#tO()Rz_uy1-Y^i8L!DSWD3%-Eo*8$+O(xE?p zz~c{xb0}d?!*sW=`#(_`pORS^2J)>^Ny^iF#w)MH8RZgZ zAK)kRPeD#ci?LQrgCi%5jsr}-94ebg>p`0mocLV)O3AQLbX+wP_Upa7I89iIaH66n z+M!BbEvf#uubPUk&dL&!;e2?cP*5@elg^%mSwPb!h#-^o0+}#WD30Rc^2=Nq<8wQi zm~3zD`ZjOO++3A9y4BfS((17#9R>r37X#Jz6%pHUM&Y2wc6AIK)@#yylOM((ssj~` z{vE|o<7r|in=*4}ic8s${o`FP*j*Pj=G}Jbp`IW6 z&OT><_S|e>A*{72QcZ9ObdQK%n#?E$hJ;2z3Q_)2_W_>JE%rz;$%hEet)F~%%!izg z03M|jOJExDFK|Oqo0fx-J4}5kc)dh-H@^Ngzt*WAV!2Gg~sgJfeIpp|kyzj>-^*MrB?FsIqzdc1iM#k-rVBb!}F9Ndb!jz-aJAfryx1n+xjm4sLYzXwrkWA!~4n0HUmi9~{*y zd?W0kzT(0^w0FfGrPTN-Q#3x75*#ebwdj8$GG%2k+V71-O$Lhhf{~b1i8sU|uXvVZ zl|SL+$G0J$Xy5#WP)nychVc5bm{RDb_C z#w^#nnOsn|;|dx?z*K2{uKGmXxCEwCU_p_9hfldC0?WNGHK|Ta(iP03YQ=W+`riJuRxuy+kcxmJ>dUj4%rfJ%Mpz3Q@);cJ#I_(vSG`qUyl>E;s z^Zt)3s|DyxDsB$EkZZ7exO_QW*cUp1@2qZIjTwjXB2J_N4(+s5pG38Z++IW-cdh&{ zVmK;0T0U0sw+xTQ9wgZm+hl2zr&T`b#~#c249zdxbvvIG8qg{^OL@Q+?uJtd}24>hR&1{zT@E!vw|enL{TioE4Ye?^yw{`}8$7l4V@GbKTNd zchl>ksYCD3@`Mpm;eXS{ACvpdr?GP@6x3J>Tgc~qc8|`iKlbrk{kB}jY{imZ%H_t@ zpz5JxR9f)6xLo?zD`%9|h)4X6eSHc5JCP@De7OzFNouM)MQ;0@kC_|?OzM^0$GmQX z3Vg2Zc-oLZ88E5-39&-!%P@pb4kq8|t<*YPQOBTF^p(_OLMRm^Q6VqY_HrwP4}DX) z)*eE7PYfhNgo{ew`o2CAX1f`p=v{W;USPhsPuucFNdgaX;;!J69?raR_oJ4W$x1uu zEH7=+xSq@?(J$^lxlR3PLSo-nas;#9w3%{-$<`{*2rD&kGo5;w!K(p?$%K}F&eIxA z{1i0)8%|;R2TjOVSC=L?9oOV|YLv3}&rE6t-%-9ec5e>ZD52wmD4&!2KYiw;YHo!Y zcp%=2;9L>KCLDwc^*W||GNZvqc{MMnZ_1adv(Z5j$9kW@E>nVg{GY`9 ziEO}`9k+p2aYID|TjeOMLLu|La2Y_vd+=`D>aX7g9HXAPeMt$U${|cTGq5W*R<93} z4TupyBD16vdfr3BoLG(&+tQio%8V@n561>wMD{H_@os-8)F?f>n=qQUHnk?9TOq3Y zUF531{8Misao66JB6wQy`Rm)q3$p_vH^zd}Qlr!r#ge173;l&^JOEL~H+1+e;)l|M z_zo$1{rJQt^MDrAc&Jja=Ldkha)s77d4z0Dm}tqw53hXZE(U|^Nv+M>il>@YH#M`y zo5!-c78P5=S)CmBr5%G{w}{|RvHSFt9BzMeGWu}^QqIRkWyYIK>%X&%#gcYhqjuDVJek_2h@n&<0B{QT zW&)YrB6>s^=D-c@2^*z887`d;Y4O2;K@itIKETQmPBcHCu&Kuyn8F&=D(sq{Wg!$+ z(W-3=XORkDaFOM|kl68OG`XqHcq(h65hYBj9 zqiSrZVaT{>S02`nZJGaFnau6`y6`?pE@8CizW25xR@74dW-RYEkO{F)ER&M8_AAl+ zRctZqH>miFFz(Q;yW_5^VL5Rs+15&zzVequ_K6vSqPB#jOQC`m-@FqeAdM2WP9;3K z$&cC0et4wZEiO8B-MaA|h%GZ~TRSdjWrkvh8d^w=B7*}$Aq`0q(s=ha?NT}B39cck zwQ-WuD!;2-W{j6kI9Tn{BGMD#7}!UE5uh+Hz99%dLt`$3m;p4@f4A*e^02XigD@hG zq?sS5M&;LPhMH^9hW7>hQ3!IDL2yX3;_8DH<(z#xlnCD@XebUU;-mp#)-{7JC~Zl* zcZm&w^ADImQ<$cA)Sd;G@~Oefhn~;#w|lz3R$m(Pyg*NEIlEPGdoMj=Z$`j$#7M+o z-oF`V4xZ*5z!^Z@bM4LS{En$T{h(|Ot4+M~+|$BE;I(9u?x6kJo&`h0-f9|2ddal+ zmbA28OPzs&dXe|LEiGE$uFp#OpKMG~3AEM>0zTWuwmz>PZ~3R(=m^VY*7%6#8Q8{` zH?~0EX#Q`@-2iualu31R%tk4#04Qy+LMp(oxEL$$6x~^JepKkjY&FMOTyHIrsZP+C z4N0J(ZP5JT2`~68?cVFzRWsZFO|e76+}iK(E{Xs=NKncGRALlmth9Z4{Yv&}E&YUI zW0c7PwywDX4CO*_CnplrmO%x095nz1RbHfgqHrU3#2fCQ_;MYo@v55_p;x254Miu! z?+kJz(a47+sHgp#!Y7s-`42u8G*2+^(s{L8dh12rGE=8 z_#L4d+f{$(oh-aDj;aiWDzkSJxAGNYw{uW>^i(w6U#?vfH6>MqAD{w>73P$=e5H8#@Ugei{)Ru zSSXKg3aXVUT{4<%goOtN%NXaN{gDhr0t!F{01yGl-Piz6Qq1OmjA9bN&_G~lXd$2* zK!m)@M6gG}zyeCt``VT$f7@<--Y9EdSiAoFqqmcqO~-1vod$_>6Y1??`O#Vt%x2C? z#;vwd7b+QB!A6y{%ft*PZiS~2@srK<7qkU}2L6!FP*IVcpCgJuYx(z5^AiMdm=6X? z<6s5^zjI1r$r-jk(CUV`JWs~SAG1MDv^xZyte)CfWh>{czF%6*7Pqbni!UWCuWjy> zN=6#*$YJ}ig$g8EZIiSR~@mn8w*5K=Yl9DJyM?Oq|x zZM=kjWU{c>HfJ_RAOw2ir?F^Kf9{>w#O1MYoQh1rx*|+rs3(5IA4(vOoQ#pq`Sdrs z^-%9{?)V#1sygl%4!%TiqLsLhPTNtMOzkmzTN^%#l86)sE??v|=PmJ{EQySc?7l#u z%;f7>!Z;k63xQYvby{&91u{qwCt|Q-yij)C4@Z$4Pkm(?!!0i37uZ2+)z1xD^Dvl2 zq%RM3>9PiIv17V5i;T9OBgppP4$9G@!u?n z*>6Z_eblB&6{_2m-Q63b7a?B(iG*FsNK`0ijM@-mL`r0_vA&DYreFG9_SM5^6qk{< z8K)u}s{3+86_$`*T4v7GctAh#b-G?M*St`{y6$Oto#<9YYj3v13;kM5Cb6S4AEgfKqlrd(>PQs`}DHvrx{pr&8FNRay5Jb! zJ_W#zcnN-YH3 zq}p0K{A&2MW9$8u)cr5v&-#{MqQ6Nf%G>yV3sKL?AGE9xYKxwNC|awWkLB`%0JOUG5)f5#9l{8Tg4uj+AiQ3wl=5Ph7v&CN5>{qTl>E8rZkD$~{kgs>A z2}GbUW>Xvnj+mX$;_+=zgG~9oM?jUnGG|Z^Dx+K@Mxv*7$u8%CONEhdgAR#Jb&+sr zJkSX-F&7ap#)07jG6C)^MTOGdL<1&a3@ACDDyX->nqQz%@?@hdJZ}0aBAq02yrAAK zr|cT$Fh8bk$J5F)qgM{|mb?G$Eyq4;sWb29z&FoL&f=gt-##G7)|`IoFN$5~IO{|N zgQYQ{y73cO2O=!g@5&pmx2<+GcqCaHxtovne#VQ=uiVaFpkq~WPKdVxr#)8L3zhU1ZO1LWG z0=qmruFe&UZmlXEpfk`f)@*Lx$WaHjBu4{p^sow4#S;7OX)UgATh~TQKH^_g9qUww z()G{^;QvAATG(!SyOdu4^0jpR90jXOF=32;vmf3vxLqAWZo90 zWQ6#_Qa)A(^sbX+iE$aQYpP1j^<@!@!Ee(hG_ee-pj-xo;anQ*)y>e<lM`g?3kf|-Peq#Q~=EQ(h;etpZe^kCurfwl|1N09c z$3*Qg`zf#4xnNi^MfVJMiU~|eZ6;^JMS+Afv;8jSTrAS z=&QzW1Y9iDle}1)C8N+;YTM+5q%0Hl-X4WB7Rb+^{q;TXP$1vRuPk=s~|u&av6 zUelFs9Vy?$5~yu?rNr#Qb&c^5r@w~)D$@#yCe^*G^??YydDDloSj)#3S;Ir9YSzSq zWu86I%74&bl0{NqP_Lwr4{T95Vhge8Ol_0lw*aCdt~`0fv(gorY~ER=N*GZd-Wo@f z$%HnaA?a9RF;PHcDaCs8fGRv-qvv}_@|=2V^(()mnh8Rhnm0M?qgLB>yQOvIi8Ae6 z#?ufW5Wswm_;uUAmoFtH5L5)V$LX#nhBo=vH{`e0fKu7=VWK5TCx!Yon`_0bD(!^8 zyu!dd1rs1RWykTNaRB5U?Qnc< zgs9bSk0g%3oryr>Q3e~qtKHXw7VFE$a)tAe7=i{F!rFz-{s@e7tC{#KpUwui`gz~<0rWSQOy|yhSz-`A`7b-&a@k5}mA#tf zuQSYuFUm@&)B~1}9|^e63l{d2g?jUULj)<%Ap2D!YG6e*m{8S9Vm zpEf&mG^B>UuX!LL^OuHOw)cueaXEYC71^`&ZnaWg_bstgT|V#J9&cay6_ zn-P)yVc26xvFq(&-BZ05*nA`f1%3uc$Sm+DmMpMi9i7qioO{N_YQF@4gsj2HC8X(D-GvjyCA|RlCfAV*4f`uQk&6U*IvtS&9_l%)7co z^r(t*pA`F0W?Zf5##p|EZS!&N?cb#DuT=(1<6tPT2n%;ItzZSm-^WJad84pG4Wax~ z#k~7nX#!%fDAR<;Wm2%G0VXXb6YIrd$=IsYo&|dj?Z+4m95OWMbWbOBNx?83$y@{y>c8-S;Tgcekz4A zn@CZs4rwhZ*DZEx)M@;CkO1?upJ?1__PYoDNk)06PIZG&hO%;Ys!1vH;;}ppF7+?q z{xFiPJhVJ6Ok&~&|75c72k35W2z4MwRLl@I=|#M^G`WsNY#Ltxsy%5qiKwEz$TGwn zEvwOY(-OMB1N8?bcZod6e(lsA{6@@crc$rEH_v}HsrV2x4`Epk4Yy@6#ppnl2= zRG>;AhG%%fyx<%thZl#Rl>0e$XSq*wWH<&*g_P;U!=DOHx4U=<7{6!0e@zDCi8?Zz zymG!l3%G>4O}y70_8jmukrIozKkxfqhe<>?1fem1Km}4|^kXlY92k4fs{mhU8jPJw zHq#&y?aUSqp=hNKzAH7(SM`fZ0yBmw`Cz$WAT*ERkAIhDLZ6T^;)~Fc6+i%mqy=0o zRh%M%+emIDx69}&#EeKbF6D7KI)dY!TchjxR#(wNh{kkmt)AD+^e+WSk0F56ED1-+J#Z%twJ&(`ZUmDPp z{fj>`Q#mj5uSYd0JC5^q!sG`RD)s071V3q)AxkC`nHw59Y9TsRUh}8u*Eb!Wj8d=8 zd3Y5d9wqxmz#8`RPHeKL(yw?_RvS;h@>FyDbTefc`#cJF?=G25rRC(xc^C)`M0t|Q zxNt{UdvzQi76rm)k1`H0<>pGRB81*<1Ckw9s%Oh&W9c?d5gFmxzqlvScwEzpaBraj z=nLC$#&Sh&&WT@)XL{)3#1<=lX3IZIDbJ!fN@7y8Bg2bP{lK9@>Anx@iz4&YrBnE{ ztm89JwyuwOMS5ja5<&QdVX{s#{83q;zv0&t1wk^ z*8=qvXX8Z3_Kx$K`tz1KYg;TvkuEU-*G+Vnfhd0eJRyxn$n?^EB*s}4%Oceg<(_nx zJU<&5lp>g@y`qz1BHVa1Q`ies`64LRoy*rhHhDdX4U&a~{D$*=6;{kR!L2KqU}grS z)in8N?CDmJ#uu`~OF*O)9haHpm@&Yd+jL*M$!9!W-;HK@Wb{#pddth#GYvlKII06o z!BOQc0VDJHkB3~tM>kGGI%)jedxQmTQ@J-D1#DdNi%rMz+g9fq%w{yj;XP95M%?=4 zhvvWj1?R1EcWSu>M3$Av=7imSJe|eu5cI%oynIljHL)E)4IKk-4KOJv0fa#KtTdIn z{75_BpY=8~NV{q+Y-7m&c2v}>^t?3CUBF}>7UZFNhybEE8aRLxu5dR1KnegnV>sQg z0->RFs9ReS(1Z*6VE{HY#TRZ+nlrd%vfZb5>i@5<{9mlY{4cK={I6^#71PSBeD*1I zZD3+iJwxX+h@kdLB1BEKQe011MR7W`d*?6B7A2EWlZZ}L~U^CuwEwHhUoLiRlglF20aEfoW2?8CJ5-K zTid33RHSxj6_uYbtDmH(VXbM{8kr3Mn-Ju_G{nPEw#ZH}nZmwcBw+q;-m;35-~9=* zw$h`mW2--B9{RYd+$duO=+6&Hz$P|W_nr%f<6v!A8)JU$e z(0?ozq(?0cH>d7sy+`f7VyEvF(&$OMNqX$o-pnJ%?G0?EtEDAC&-#}bD>cw$^#su+ z<(Zz!^;l(%%Zl{&P@L?1VQ8wssQO<0vSM->po>G2v?A$561x?{)ynyWur_I>dC9Q) zvV=t2^>if3%f%Q&io<-o7mkYhWigRBzD{D~le<{95|q zS)OKQMc=7vNKFn+UC}w{G32Z9U62yRk)apX!U>vu-`~9m7No&G;{$;v|7@creu=V7 z0rwNPT$eC_NU??-iB?eJN^LxqOuRTAs5~Ovo?A=+l@K-)!*qQSZj}(>vEx+0tGI@d zUvP_A@>pb-SJm>K8H%6Ma5mFg>{{`yLk&{p74B!sH4C$vD^f_#bX;~TerJ8X`H~a1 zJ1S9}>C&H~hm%t@5O$1x{$w;m#$j&Wz^_3+*BMSz2R@2x;kIv|Nl|n4j*zfI&)!%z z@Pm~Z*Cwd9nyw`&IytGLfJ+H5RMM=wFcN4cNIxArOx;Afo#;rM$jgqAetTOV zeHC}RvAa=KRMxWP^@&CQyqbEw4J$dTS12sH)-Dd#i@T{Uw5T5=xznH4o=XVT8(@Ll zkXv$>r#GDX+7oI6!?uA);{MVVr7yX^xtaAPT`Fs{7Gg3+Kc$wQultW(4)vL)tmNGQ)!z8m3C$ zxxclS5AjJ4ZegGt65N~H4QdXe7*Jnt#vutma*Fv$?hHLk$|8&KqypO^{_euu9AD!< zSMGWOG#N+2nKOccXzSEj+?5C|iKg!N@3BS!k=M`9oe@NRh$H11w(ECOtc2M_%(HJJ<#UF;rGYEaQuEwJudBw3M~GyhHioOAz9ijHFainLmUj zgsfO8ZKZI~hX^=RrItkS_2h;^bt^!snIA;>_a?9!SN{}L*GY>@mXbNAZ@^U+5%P zH>bl)d0c7RuqI;GnRZ;`T;nG^xA;GPQBtA72JTX<9rPTz@-LT`eS^r8-u3 zOrBX6LLKrR4XQ{t*-pA?DjsiCl@} zt1jtpbEo99HT&D`K~}o6Z=&OST=OZvF>8;orcE_MLaEWN2~Te79BvS-R|AE{?a?tK z3W%Z7h;J?R@5BeHg(@>pS!WBxX*mZ%`d|^Az|;N@SHhe%F(5Ys=rOr@L^4^A*Pqqb z-t}MQze+gSmv8OHM|Ak%aPS#6_+~HD-?JyFrCuVW5G0GW`hvpZYvF6a@NuWdMk5=s zE5z$3W%@8H{t&l{nq@ZuTkeKff8;^ey^f77`$pyZxB*T@ElGK~2EHRCdx7V;6gEK_ z8JQiFj2Nn_HQ2Y)@f$xWqp-a9hW6p>-0xj61eUc<1y*VH2Ui9aiCfbGfuE8#);iPH zEI_XD-qh^DU@?_Q?Ah{ZamV+d6KW%n2LMg=w?B=e5KB)LFPlz>pw6D2TExe%H^r5f zD7)zI=p0fF8=KUM!v6qeQ#P4|R=O%T7&?`=?nq2#NY(4cmKk;{;bU>T6$+tu>2Tkq z!$)(YKo?u7kDT94iO`Whm#7Plrm*&8aMQJ7@<$(}DP!n2-p{Lt&3iDPF}~$D>r}Gc zH`sLuy3XU{4}A^&Wuxgdee3}~btUucH;HqO7>vk(lD%jjixs?Gv?PToiB6#IX*alu za-b=)3B(hvdkxUri;0S zrU&LL%@Gg>=Ao)8$Rq&HyU_dz4NKZYKkAl~JkPI27ZlvzPF$9&4LybD^M>OW1>?yv zYss>XVbHO`XTL`~Y622{L+9yS>9w4-56boOPK`VJ?pg?6Ankb^a(F|qkpL9aCvkYi4ePBk$ zq7CPCL?b|{(1|D3QE!-t-@*$*{(Hq$@#3S+!Ii7%`yXIIZ8LmUfaL8AeU~}ni%p`Z8xR$L|P zKh4k~>S|%VH~x*SZ2w#Dca0Nda{J=ob(si-%o+*liI2}JvJCDx~SwY!m%t^jPO5_xyf+xbFpC5?P^iZ*nU0PxJGtx(I z?S$x04Y(kv1LRqg!OQaY+keyN_X?XCppXRu>#d zQsmHrX<_l;?125pHp|E`sbIT&GeTPpesK=|lbJOP=nunUFP!l$Zdl!+!K(e%gD0yO zBGWt+LewrC2Em$?_^`Y{w6NynVHF&4s)T&@rAqDN@nT&a?eF5(eTqUceB<{e6Yg3Y z;@~j4KmW9uGr&Z_s)+Vq|1M7ab@*Bgxs%(VQoQBFmDsA>saF`@U`^vTlC*?CWww!K zbaDPM53KhQn#aOFUXQq|%Z}OXCnMr?s=5dx!u_T5R-=*Xcen+?{R!yS+p(vuE#W(X zG(Ccb`48}|eUyW$=f}Sp@uxtsAD}S? zL8qqgVeeJXepVpzQ#4$fi(G+Gle-W93h?`$__p4=Rx7QfTg-vB_!T>Ol7&Axd7`_N zMgHDm1@Flz)L!I@juLC+r;>{>*CKC+p~Bj7*NeBF!dJ@D1DScX>+<9V8!T3m$vsfM z!C_l^IRiZ%%Pc#9f{y>1Hgsa^oF7tMoQ z=4DifLK7T6-bklmi4_gJ>oTQo;dvApTkM|=5MA8JPvytXdaS3rdisu*IyLdQmC`2{CZemE%n?KDkdE_D~nbiUP7}D=9CwPzVVefzIE!e2> z;2!|7b>AO_lYW*^qR*YZzq&Bd?DNU>Ep{{`xk+MZzA8x%y=QS+eO+O|F)T!=PXapf z;&~3MoWd%LjWjNec1m*S&c;*W7$_M?{{I4UKuA}lcOYmQvOVYzFhL~rw zr=v(C`DkO!?J2H)jMnb|AQWbjJdV3yu{*8BSE(;bipO->Jo_lRz?m|Xh&M*Zq{S%B z^cY>a<_WDo#oG-A=W=wy)`Sm@)oS;H3UYKq?LK?*IAK)eGjcZIU2XfQ%yX+{{0WeqsY>G@I)KHg z7QiQE6du?sT?4;>Cf`^NK8W=72X&6D3Ga`Z$#a*wNc(-5for;R!4mTQg&Fv_L_1h# zWQ_AxSnjIs-N5{nKM#y(lC0Y;6B(3bdPXdA{Wm5GYwKJb`!$0bbBuQiWm(X}G!Z;F zilim*<)5N?G}*C63Y+1|W8p4#G8WY4*2k0Z%WogCT%#S)?f(E?vQDq!|jrdI+? zZ^#hy?goB61i`&OtORf8;h$afK8zJx%R+!Ea;EI(_B8&Py_>&mS9T0kp)*I7 zVz*Q}FsI_B-(Ak14J=o+U)73GUG5g@8mvw<^1@0#P;&QX7l!fe4B0inz7UC;J+FS( zfakI8hb=M21LhXOb36zmWAqf z-_EA6_CCvLAG6*r_7<`-NwBb~mbYlZfXC?3&{AeLHuv!Sg@q!**2?2&?0%%teg{G- zIwq*PV;{(Lx9eOFF&`&ywR*%t}Dre25Vkt3`qGiSpPEUz;mtR`S=W^}lOU z%9Y~bUU10&$a@|6sDeLoc-a+t6%`-f{HboF>D+iwsXqsWpPZ$@tp238=rHG-r?#dG zG;q~MkZ3Yn=clDREvVmHoRR5|_0!^pW|@tH18!6~k?2zkqQ7sjyiadQX}#I;c-$9z zX>nG=SRSe;0HQ>N$ zH}>BfdE*9}>6*fsqQZ1vi&7s>6boQ6Vn^p^JRg{@CDK}B0^;JM$WyezZ&krWY7W^X z@!o%B27ExPC@S{<&IDHd*z*AZMA@Ea@N5*iY5(oo=C931{IhGNL~2JzdO7Fm?}BpF z5>X&jClV;Wk0wbmp^Zm!qUJ`-Se{o_=xAD86rl;VATN{62T|hW)6#B6)ZT53i5bPD zdd;l+d42UA@81XvEj%TYjQ>QC_S85kQmxve^L+>k#X$wbt0p?4Qpsh|m{1Mla*%zB z?W+5BSxOyA7g(V^sED>fd+;o>k?|Y3r$3x_`=KKD=E>gaW9E*6jdz8GKlYKlEcE*Q zto|&w9(HxltX$iZEo+*qfzH!pI#qfn`{D)XPqq3RU==tkQNzZ`|E$ixal;WEM89_ClEdp~8Z`@{P(4U~AV;{KM? zO|$++nD60_ceV<}q3Xxwyb+>tzZ*5@TXuVY`LEp%CnqRMobH=(0(#cO z#$Ked#TBP+LgYnq+Y{kW7rA}_R!_u&vd0AgR8a`S@Yro;b`|NWJ0!vHB({xHFSxsQ zmN$g+=tcRUNK``F=vM5}z4-!GZSM~hXC1<_L>)^lT$Xkm&R}1KQ5rgkZ_H7QZ|EIFr89mPi)S2bIzLn28 ztV#Hh!4--Z^k_j(8=KVVZlXcSmKEONuQ-`x)fUkyOI6}=i2}EI&0Zze43*YUJh8r`r8dzfHchGm;W4*~@kjr&=q5K7=oAAo z$Nx$2;L&*kC;Quoh#6lA;o|3p%Z4mVz@|W2a2ovk%67?qcxz4ct=v)Dn8CXle7!ninpC2F-4Y z=IaD{_*nE zU!PQ+y9}VKrANtW$1JX?`T3F!ZuVa^W$8{V&Q)Yq?OC@oWK@p8VTFCfq@#6le6*L5 zK%4$D&iV4W1OI8Om7L-CNy5`5gOM@434Lgkb0-ya#6(JT<{%!Iy1ippp|YT)*yg#Q z6#u~3qH);$JPA!TY^pGXGvl4joXD4&(XR7(%hvao?vVp!jug|9qFLJ>9h&q5ZGPNr6eOVtIhoIuEOBufHn^_CHXvvl7OmwuX)c>qd6Ue&7#H`G~5*Ub{UrT*H zES)Q{{w`O!r=fd-(Y#2px;pt|wGJh#0n+D0fgzv@kqQ8&6Q~WjTaykp3g`GRtgm!%kVzxHsO$rZM?Ss za&76Brc|8$E>!)n2EY;yjz@KGs}RBQW~ zrzosbN^@LJvvVJM8MaMkSr@i!_M5(~J(zFWxGgM}-w;N$xR^+&as~S_huU;%7N{M$ z>%gq2_q8fpX4I*XWc*Vy!#B*@$SR70v&ivgP))jZ83py0zBjnc(szZYM z$0Bmo%vwOjLf)#fE;xStOm4k1QnKKIOMev|GBfo%IG%Vk?4jQOy zEL&&CN_=^7(88P>D-S^S3GymeTMAUHaFB=u03D@v@CH-wla?1|QzA2ei%e+G4qRMx zunbwLpHTc*qL{wpNK6v79F>T~IvbjvWP7ZCC;Bu@dIwEs)(0i57FR)~%)!5Kz8d0Y z#}I7?cl_DFuWTc9lJWEAuCMF(bx#}6WN(c(&}d4_`+#z!a_!q9h4&<@&%iY8Jq*VA zYV~t1DSL*o(1-FjZWqdDXWlR{nIMS^DCt$}UG%aQ)#PsJna`8F}WyL<~?UEasU=NGfQ@8iJ977DC}xd|ZoOD2=7xXrn;jFNm+9m(;XQ z?mMlGy1TdA-Xc+-{IhaMCN}NXRYaEJ`CAo3SQb55RS!EY7$v;CDhc7vK0Ftd!coYT zuGX>Bh^61*^*53_-_1y}n2!%QAQ>pdV*iRM=yZd^44l-2-L%dM?Up)XaN-Iwinx%mTd1aWmKpt5*{M;VU6q zPcH^{_gZy8w;X^ZT!rL@rjrJ*-yyYIwVrBY#G< zTvQWe=X)Bpt|LP1_v|p2c-YhjYSmWPbWID34e^-ad9OJdkr!=whI4TE3gfMyShf@W znQtj^PSLBiGZIDHi4wZ;C>I_SUf6in-qtg2`B=RA>nzy?tt_w=5qhN(B{^L3?!+p1 zcGLG<=NW8Na7X7&mr(5pz4<*54Ge}#B8a^lL7D_~>&f3?98{+Z(yJJo%Qrn})E@Ne zxwgrzV(ijOG7OF&_Mgb6MReld7>c?B(w}&Ov5DLDALbBXd9N!gyDXaetR%dOtE+Zs zol5*GsKWE782wSq-n{StWOfcR9rkns*MxpB&C8@gO-YvOWqSSC_TQKOHmvn9Q86Rd z13}{m`SlbFmNwNl`0^?9FyoqeK4OF7(EQ--`L$9RxDTyTSM+jYC}I#h+pC7>Cc!<- z>N1K4C(;D{w-#w)J$YxB(hbvV=6ffXB(k~kLiyfOm5OYnGHa$Rn*~=oOSd-YsuLY~ z5gi?v1`V7rU}I@)LE{_@EsA!Gf6V^!@9vx&8;%8&;}h&qK4Zk&_fOn`hWkZMV#}4` z=R~1pL_nVC^u%u(FdzDB3Jp(i##j`YG94v zyn(?Q`>OH{Y*Kv=ZubWd9x>-=g?_0BifAoB_0^C88FjgEz2rwa<+_}07k5LG?+cBT z!bdasM5o&5kmQKBEy7Hg7!RPQD~9vQd27c z+<)o?2__113v6ypj2V4KcZ_B%#7x4TlGB+xRqF+=_p-XXWd<#w?q80Cg(;z0sc6U}?7bN)tROFxu0S!nouD91$s8#@<$_&$ti58xkOQA?@6>oEAh z-S^>Pa%!%-o&G$J?#o?FnBFLZ$U&z?dhogf_~lsq(MxpfHmO#5^i?y@@$3ED@F|6~ zRgAgeAW+kqt#^9w>5taABS2HQ&^#nGLR{G=5@RlJ{@9T;2 zU6@KqPMTMr?AVFGxo`e{SnbBTEVFxMo5XRnwQBq!L(4%$q#cE-swxL4>*Dzb0Y8Wh z8vISnS^k^#@>L0MM8n9ys8R_E5*I>e?4h(ud*t4Zdhb-3ed9)^Hw5IaKC3$8 z!Gw9&g64-2bdwJ+1p*)B zj%tx)v}~|p2{PQRgZqtpB(kC(#61cR_rRAAk6huO*TKncOom)=_=Bv7#n8ds1OYRv zMc&+^&B~-KJ&<6fL)=C|EK<0^YxQZ&F)s@})y3j!mv!ha6eB(bJ~^S(I*cZA`l-j=eurdik-`7Y((5MVvoR! zl<@+K2^pk2Y2q0Gu{HmR4Dv?nkE}xXb~u>&w$_u2w1QoDCbBJ!xmjHa=#tCtazdQ!rhQtM}rT6BP z8br%Fe%3pv$m$QVaohifM&``EYe>j0NA}>J4JwA?(2m|s3ijMn3}yLmse)bDp_U$9 z?$KFiEdX~B(T|f!@f55oYiw4&bgO2=R5(;RClD;LA4*@u?jFNGOgYweWS)BmDKBZp z;hEM(5zNsZBXr6Qv-o(Jz8I`Rv7%jXns7d!F!Djw7MM`s!(mj!k4L#^#NAW@^cS>NHbBC($nBCN?vY2Ruuh)M7;|ESa zOfthGZhLGKUbD{DC*b?7{|AWeZB=hn2=FtabI?CyN_wrK3j@0@RJ!cy=;;e-mby@9 z24kVJ3M5(pdJs$k#W{&N{&f8%e7J17BV-BHyr}r`lA9ZA#M$w)u77hCD7wQ(!F}oc zYDM%b>=x*U7SclA<5zhB!Cv+E+2a%~qO^|&O1&=M{>m zsfZWBy9iRqpwCI7SnE!u)|UTXcAI$%en*{pX`9b#$-2xFL{S&f=M+~Y&uM=;OI`S@ z6D#TpgUo2F3DT2OM6Z3`s5?<=dlA+6zP2G|DXUwEV{|{~N z9oA&m>}~Q0s(;pBs9f>f`E#EhF${+2~q+Q znu>t*k|v?47$AuO34|W}^1koB_BnfhXMbmZ*LBX{$-UOP*P46Q%=~^cW2~G)|8hHJ z@)Dzz0vosYQg<|W=9F}DR3NETlM?b`QV85-*{AdCsNuD5w*tpAA7?)L)aCSdgTF#R z8IE(}C$8l$ldf8W6hrur>8X&_$3H^OzqXt1)d})a+kIT(^jMZ^ZTC~gx$F&9pd z;{J&Fip$=B(eq&SZ$s+eM%Dr-1Z^EXuH$j-dZmyZS0F)ZkB`7aCAN0kstt(Ch5ZA_ z`j5QEq~88J{K+)MeXff(S@!^`~EzLs@KI~=O+$xj3s2ch0JASD4yXplc^YS&Y#I`G^ zdzFN;asF*ahAcOq55p1%zlk^OI_4<1*m~j6)0SX2$Io<$Ly}jdKBZND+P?gtjJBXV zb?@-uqbVKpzZ2O*`S9jw`+H#V?eBBXeh|yI=YBFv7Tj-cYdvc7Z?2@Q?U7rz{#O1$dgoI%T{P{L9iKug|ALL+7Bb3O&wqx^ zY=8b6I2KoJa!k#YsQT6{nE#+-|0C+ATHI#|_I4EZ{Ornuj$fjUJik5JZrk5~U8Fpc zQ>6WZfJ$lw71DpbendU-_ID!4`gy(YzLonL=kAz)iMo5YI8mIxYNZ91JM^GqOml1Y zx7-s%)T`LL7dm%`?mE1BS&H#l`0ei9#P{Nd-$ZbJ%YLZIota*G&~af$oL2VgzDazC zV&Ue195!28?1^X}x3dlf;(x>j{l4%qL*>D-%)hRRxld%2>yCU2$rW#`hB)ruWS*fJ z{=AnQ{VgOlT-=gVZ;S=+>lkkt=U$L|5#e0D!kXv{(u zOP_7Kn&+X&7OnG&c(c(UsP&}UUAZEuJ-Y+MrFWLAr@Y=jFD`w?^<=L(ZO69<9hTmA zmYxIK(aG0B-trQhzCHhG>M4`$ z8=|qVnfEtOoKfy`?=;ho&c%dRNPqsambVIMR=($M zPKZf)-c9SQACe%wvyON0+BbI4!Z*1G^<4A*xS7f4Y+w4$ zvJ<7GFX3@bZcOQmlM{FThykXg^M9=wezxS$iXuBo)S6`jr^fS_BE(laAJj%(+45c0 z*oqFxJepsYb0L*ldvm<7{an%r>zMXI$rXytstsQR=X~1%E(<-S!9BR*x_FuB+kJ6O zY+2_+`y59<#ua=0Kos{sDR`Y)E90R0T{Uh^l!0r} zaqsm=OSTR4+R8cZW`pl)@y_4=Dwt?>^DlWTDQ7rwA_3~hnC!HZS+Y|;-m_sM<6zdc zW%EbOZ0gSG{>tT~)y9re?|k@MClT~%dGBuRUrZCVIH%PV2|(VymE>k>^~q(vM`aZoIiPzIq#`l@U-Fz+PcHNX-K`HD`Nuo;OpDXcyp)C z4dIwyzn?EpYx?`Ql`DV52wftNrl&Hqt7!Pqher=pMAASsuN35pmE4{Z-g!7yv{C(K zu*cFZ%_bKU_vy`s>SEr?-Mu~>!q!oxnh$>V^50&GiOO7y-aP8-%XizC%;%0h2Rx2F zikv!a613&=;t}QXx%-5Yqpd+9&SKYMR?HXI5g&ZP+_;>nAUV6gIc5ij?{5vwA9|3K zK;Yi~C5StDSg!V8joqJbbF&U7Hdn8I-2MP3SkV6NuXgv$%#YZGCyFxLwm z*xa^_g*V#_tGGc6Gw_&%&5fwo>=Vwf;BwxH+v+c930ZNql})bBY2RY2<0ez2#V;W3 z!-82QoixgeitNhAiI7ThO@QV^J-uA|evR_8mgSVYfy^Y@UjL zh{WM-lE?UV$Fhqv7oG#>>KGGRtafg{i4^wSw!GrF*5_~iyY_Z&NgWRtKl4jX-Ix7G zY-+!mT}2eTsM~GxsYn2(wwtLzM*mGVa{@Q)bRrUheC9Cz{`BdBY>vortd)O$ABR*mw!x%Exm?@+Tr zQ;}eVR7xIw@ORiZRfx4MP^M!&q)cQ8^Vcu_x;Ua1^5VXEi8AZq_OdbE+V&X9#70*W znMIM5o^xIPuDsXe@uS=M&LY7hR=z!>7A5oZLXXGN7FWi5>QF+2E=ti5-PyFGOj14^ z8>V#eo0RjpX8m|kU-%1m_3Ql)%f0#6MJ&rJy`Hnxckp)AF`-|~xA4^Ko9{o^LsQrD zFYf9sizCMV{^!V@+o*BX$Mm=OtxV8_3D0(6Yo7n@oAk`1KVoHh+1K{H6{#zc#-E^W zNS~eSTsQx9LP8(cI;#IiEL~;4##g5Xk+|h4owE=VPe9+DhlSsF;GQ*gEGSA$S+4XGi z(-0ju)s5$^La=odF^`S!C(UpFheDM2E{dPhS#dx7g!zQ`>08VMxHat_~kJvt) z)ZP+4T|!rQ&2O_k@_y<3>_63Jk4TAb#k?Q*#~MRHTbio=yYzz0;tp-BMRpc!GZr)b zVY9I{u^A|Pe{5IA3q+gdUyQOVXU;dK^HF7~(#QCR3yJ&xD#d_&M$k=C=)utwb6VS< zuP@xEKmRsSH!LB3TS8p+g4+My`>-ai+^T^?^d}wR##S}XFrGzVWwmdGA$hkw0kd+O zJZx-%tcCJWJafy?I`1;y*u<82QbM_wrb>8j0rpUc|8coRVQ_&;s*CX5Hp*hm1wN`J zZ-nK$?%{OQC13CB&u?^_65Y|(=Z8tcJYoNSOJL>3MAn6xtxm^cyP#+JGAVPC$LjlH zs0>@vbCV55UwBddX7>CrV3^`sVWi8K7Dpfls$VKNQPSWWv|rKC66Wa3pMFLs_Jh-X z1ocjkI6tqfcg)1uvh3Jrh20k7vcG)yO2_NlI1`r&uz*(f@*4Ff=v0sD`Y}?N;Z+cv zfl_Xe(P5RqVCCGg(hAx#l>!vyv+Jch!NJ>YJu!`tQ=-;9vX>@%iG^!>y87IDV)pbo z-sR6+42ZqU!COCNc_h&k>-u1{vCp&!=1?$cM8pv&$6Ilof$sFOGbIpz_`y>^})k)P>R!6O1JWz zfgn#<%I#AEJhi8o zR;O7APCmHGjYsQ#w_h5PdLQ1Z^HrTNB(Z_bQk{C zJ=NU>cy1IInnNv)KUzOEv{1sT{IpSOEKJA*{}FTW28>Kk=N;(3 zO$lRovDcTzPM)j{N-qr}SK1$Qfdn9!T+iV_&vITwfU*t&U;W~8d#G0VToyUMdP1P# zkiGS@(^)af0jgLH*P2mZ%PKO;@qahrTWl3|0N>te?Gvn*F`BfbL-M4_->je-UH#!} z35QvD%U-fw5FgA@S_Bp628GsF=Gf;iRobKH7yKj$hNId{~C zu3>ZQCJ5j>hmrXVcbBopRt_Bu$ARwwUJz=ktkuNZIq0FK#UaBJL5C9aR5mQ*bNS2!Dizp~2gNgKRGJ31#JtORANxC6e51L07r80=>DqMIG&ulFRW25p6$eet#teeD5(%^^Q630$(i0>A9A} zMN3euCJ|uJrBk&5>&>8T#hL=t`9ETUh^4j_`h2qcOGnmUK1W2<-p(QKK~~fUOF8&aK@uT_o zaMfa-&}UAFM79Ob><#uE$JRof43UYaH1%zM#O)B$DjQ>m0kM;~$I+0Kx5P%bNw-i} z?T!g8k0<*rWVT~A$63Bdkvj{gD*=&7g$*t}4(eAh=9afmyE%c3mXjoU;J!!9d8X!|`d#_BD&Z^8P@IEa03!7eNzlEEHD#;Yp)_im2= z7)H8l@$a7PfBE~7{wVo3$G0f<`fPxeLtT$U&xuG8T&q=<+0+`M@#KM25|oS@HDXjvi`P)Pj2tKeL6x*YYB7Xj~J|Fcp|8a0*-Lk zq`7*d#vAQNwDe2D(;<2SB6}x3xUK}M83-s^mJ>Jampm#*zTOySOpJS);L&`Uv6zsp zXkKdeXl)P;U{AhgZugnci_ncM>1*Crk`sn^i0mgE3HJR(gpIOEb(PhjiGmNU&3K$wRoqG+p_^&x22$!t4#~< zi8bYw35@glsaD==p}6mN-vc>&GEZ`{0x}PGmExP4cpUDH={uTpisxNNN%H8|S*(u{ z^{_EdY>3p0%N_qKAvk)_?Bp0_I`!-tfnwek_hO7mYf7fYTw$)^2UH3g7(EV_UZs(l z-O_1*d!$hv#b;o1SBS9=M$0uU>urQkzGshFeZf8VuN63Y|F9lzvJLQ2{6g!aI3rQ= zfphEMxlbay`s`_KZegn4OQSu;Pe?R{h;)|-8aVOE=XMMWS1EtWH60j19T)}1kbzpY zRL0mT)nCEXU$)}%=qI!F_#TE)@VFH+Z9#Fy@Pq(0G-?X9am;27qmSh&D^4iB^CZ+g zz-mTDv#>Uch8auf^6+90xT$?XL$R)2eL!;mT413YO$Nfba@2Y$3e&(lR+7tDLI;gk zDpN9S^hvEYW5fu;F-v@AV2MX@(J&*4hHHx?v%4W(gd1IvkknfuXO?C~+)D($i*qZ8JMA+5(j9 z8*Wy?R6;`mZJxzq6Zu2yoj~u?^O{aayCMqbYXKH}lLDb#J&5L>K2s_6aim@nc^IOl z`hFA^WOtneavPnKcQ+v8I0xWKgkcZ&QSAZRnT$#Flb7wdoNCGb{}iwNyYt8S{*6k9 zoWD?c0|e6$DAt`{RH_rsbx;a{#b(u#e+>1EZjg_3WRKT7Rv6qes5)gwO;lu#8ZZ^QeuB9$$A9j@bbfS_a-oPefr8rg0}q@rK(e5OeC_5%La z_@|kO_Z|Qi&@TpPWo4O!qLsa{eEpAQ$;q_J6JcSoYcaEoe3hKFXSfENzT6fgM&vur zR-nHf%S$kzg?9hxk!5pk{ViQl!MSe#G=^cQw2$nTiFRCnR3~g>zfRK zq97u?4;00=EF-9wu6-O@#@+aMt8U~a7AZ6>WRym>eW~S`kF?g#GwUyLO|9Is1PH{` zMpX#Bw3OyWq)bac<^#C$Y&L6;H!I04(-UkK{REvsoybf$@DayI`HDjvZmd&Zew_)U zMKB_?jRPDk;Q~#Xr^-dw7q4GQ_O3>S&SxFQ&IT0s`-Dw`;ak_k@K^bJGTfWK(wNI@ zVN@o0ruP&D|Jj zR!J`1aB%Q$1d<;(o_JE;sRX+$yD%G2Mnt+tXd(QxKaJll!4m5kgVggNE2x!6_bJVQ zlbmw;l)N74^o3EX;P3Z!xiJ3_Qu%JXXX1YUE3N#mP5Vz+(2fP4Yk8dQ`(qzP!E>ya6OlHSL90JCcMF-?N3iLZ4u&Bt{bJj^$0x6H zl%`+eX#f`|@}eT%0?29B#v#WKl+*)Ub=J|}9iXWI?(~1!hktEOd<}R*_PKD>RE9zcp(jw+OO@<1)SZlBwrF#rioEk*mp=mm8|*z zQC{oIK1A@)idGKy${>d|stGJVxkUOQfWMmvB8O)113as}iF9NAe)88uFm3}#E@$|U zs6ABuciZjAD^$|LE_PD>Mx|5TM6m$XV_ikf)Pu4dpEOfOd>g(7*25v{R(w`DYP>xY z;+Qd*0S;#?wsNqe<%RF|??;)zd@v>}hYV#Q4F zS;|eCAQNI$>n|bE=9(1{0!gidsSaccI1)n z6iaTukDxPO?@0#LB#*U~!D{c0|1kHq1?%((YLG66}ZX6L`5PT}0l*#d@ zx6x1m(eVnZJh`QYFDbVKi@!~=O?-a4U!!0fofV-I*+taT*8=Z(>-}*Er4#DW+o)1F zZT8Zt2uvqA-9m&zpy2tq+CUFx;*-v^&Tvn9L1BZMM9+o-LqP%_wADbDuuY2OH zQO)p-e&lrri?T?WLOsSQxGq@(?8`=mA~K5#{VTM{KFBRX{5GFIptQVuL=OfZS~}ttUaZmq6eol&jWlJX!?SiU3X9lN9US<3kLP10@3-& z^fsn~s-Hrmx|YEFsRqN;l;t$0g^bh29X&nWP0h9I-a=(9TCFmiU!tcU^lq3_Mgeul zM%B9~f*-3jI%0|qn5Xs*zsh_ydVMhe(MNF&^HG%=FUs$7cIbK*kYw12X8uG}^?WHP zQ2=QdVO2^wJDPi*1}r2M$kZkL5tGCwso?g`U?*PWTPQyo(WB>3t~NVhPalBn5ZPku zPflmxl-zWiWR+x0Xxslj!nh=MF$AV1xlQeZNc7_4c)I+e48Y7c9$kk-Kio0|&H{%r zF0zv{bp**P_9HsvN((w@F*O}dZZ1M8qxP&MpTYNpa{v&=FT3s|;f2@9#`nuhuLQ(P zUedEUW#rc-@vzH&r&tIx(V=SW!Cx12AgZhb*Hkxsolm;OYb!dk`|lI~eO zRFU^25q*1->U3c#XXhId6b000*4VKVmHZT@H1#IZ!+UPtbOlWa(ArZ#_#W1eZx&Jt z8>&JrB(HDl!5X`_B{VeR;$kXwr6 z9Cz6jF6BZ%NpV4@#k_Fz_)T|V1E3W5yYI;-T5Y_%`TYTHqsUu-#M+J{`*U1pj*(-A zbAkj%dh8KX4in`_sD!%Fy_NO5g9=hEr9m{U+mxd0A+Uy{7pJV!5xu`I9NAY_S^z7p zE25}9>A?jno*MtXjgwh4nV{>)Of3(3YNXkn8wXu`u&|GVuI}tImG4c4+@XWh-mDja zki4EmWv%kHg#KnwaU~CX)nDjzw<>hX$`fBpA~JxLTF{#J+@G>@Moy)GmoqZUCchNj zu!_moBW4~`iW^HBorYoHq_p)8K>;$=SwXI{Hat7%aXVCtt9$s>J9x7)?A>S#xG_!M zeR&T;tEU`W>?LlHVy9absKZ?9Vz`@O+q%#TB zm2OmQZ?j^@UJIGHHAzLC%_Q@^fePsp;>A z)esmlBQg)W-pg_O9y6e<*=Jmm``tR>*QT)G=eQ59nvJ;ygF2N55#TxY-5fZ?>@{x4 zQ2FXOf5FB9e~?0>GFx(cZsOJA2igXHEDjBP9{911qOAZlNuZlbMZ2&2VP|$eqma$r zz)Dz3G}F3))kkp*Nbxi(6YNMFsH^}%AP%NP|d+$uxN)vP7H zA@e&ck`mn3HQYJm^`sI5{7};emW)PuLtoE>>r1`{@E(KGYIe%45!4Q;j^~V1-;nJ< z+;_V~`^Qq$_(N7JKgNkfQ)hps8HjIb&xuP|St%nP*(+tnR=fU9D zp$eB>y*L%j&a};%>12a!R>WD9fFlgFn(>$mkCG5`2+zkglFOK5fi(?ojK&bR}2O|jKRBh1!(8GBw$4eaLf(>T& zIPx(gl+$==*q0FQ2jV;^4j^eO*9n1Gud*AJ%MMnY_{1M9 zMKBa$tSet|T&~yLCdU(Xet7;<@OMfH!2M@w{LG41jvq{=1l>)`Al~65wWJSa@e9oe?UT8I-a9oHY-E0zz+= zw*ZYoLAni-J>$l`+UNSFUkv&nmLfO@II1;1Fym}QQgh;a;0Q7P?K(ytG)UEnudaLM zE(n&rwDM8>l7jo;iJvM(tCjrSJp($oLZ>pb@vGs;pPh)@Yz;-x(t>HUKqawr zZTad+tWl3KM8@ttbD1ok=Nu*A=suv!#-*3&S0B*@`&k(rTXCyq?vIh7OwBEWB@;SZ z!jnUMx1<0WH*pS?$Ybs)9^Vto5Tu)W@C{a3lUUx(z}B(1!(;Vy2Eo1?La!dwSA9on z4?ssbutUm7+J2ZGdj}V9i+wgUC*NpZby(`F=0iy6mney|>(m*A)8>?n+IL|TU>vH! zZCK-Y77kmZuJ%?n?#SwaImndzEC1(-Q_B88tm-ebiU(&%U@tH5Dq zt1G9+SC@!fNsJ7C?*Wn;yt^E z0m_JVY ziU?9V0cOgX8_Jn@NAKC)%$C5fv{F>M6nU{n8pV&D?c0Ij;Uc~IpoTuY^$+dzQ3Q2a zB9NTh7F`%x{W(DE%aLZ#<|Q+2rigKvN{n?xN{P|ofb2{=L`*8gdSqsS>=tcOa!5lsTyunG1%u)``wg)TIn0( zz9C272F}87<6d*xrOrQ+qa@gyBvm6nQf!=r7hnuXc{Tr7nn^87eMcCgyecd>bKPvH zR3QSTwM|q+Q&2UUj;kYsrO<`ZF(?TAnnuC{Y!JBLAIo?kN*U3MGt#93siLb8tpLUHVhSd7 zqa2sAW07i3@| zlk{K2*ZzmY|MUra^ambG&RH!NK!2~Xf?n47L`DvqpU|!!kLj-?3Uu5-i=!JWFbW_q zgPvrk*|(jpt?7iUsD={ffc1CpK-s2lcN~t@>OJ?kU?Gtx^YpR7Q;yP)q@I?11QT>Q zG&5htdx&5u8EK3+0t+!N){)sh5= zp1$LDc%5oT$*y6co`IqoYo8a^twg=2QqLnOOiMc^U0}$|@vYDmWbh12wf`#2(Pw0( zA9JD(yRHiYWiJOVyoLIW<8G zV8LF@in+Q-j^t%cETuk%V~CZp~fl8$;=oHM@@@>2FB5(rl={ z6muk4rW$VfOfAJcR)qKMkFBHpUTG28U%3xYn*u@^Ka5LHuiIelpS+$)B9CIm>IkLN zy4obuHY?QmA{~I1y(_!VvV6_K1>NG7d?ciy5j(FJH!fubyZuwpHF`)K$z`NM4JMv1 zt(-<0>NJ2NIU2aF0+iJ4`|LX<1bI{)n>&9FwF5Jjl~LF_87$y6BUtk%BN2Y5CmLS% z29zA5$7ltHKgKFTtzynuh#!wpN0DedeBBgOo|=hDq>Fk40iHI7#%$$Bo~>+ARY8Rp+K5@^y+A*1n-kqU6VW0+wYsM!Upjc#;;%6aElA&4GzRizIdaH$KIWyhCf z+E?C=F*$oOz<3M3O~#VBl(e7VX4GtyCCZ3klm)pxjRhF>1P+_@lU45UgDpyXgahwG zccwu@$qPN<^M*NKdMOQ==NUy+F(qoT5fia)Y_18ASv7;8 ztn-__zcZ!wK>O$NQCs!XIwi2039rhi3?YEseT{d{4dQYc(}>>l6fbo;UTZZt+69oo z!Iy(uE(^?ux6;_-xk1Qh*SIq!k6^-Jlql4S#Y$QA>GM0DwD*ttb&uQH-kPqK8n^;S zcbl60K-3!aUGCd~QrD+p+3YVzmIdD#2VTxT%s|7J3H!<;83QW8TtyWia){!qJ6RO` zrfGee_MK#GSz3|m@py;cH(;MOC&?&Dlr_$IP$c^5lm9q_?ic<~u+Lhor}PNdI3FT>286(Uiho%rUhRq4$*nD}D;G4Lzg`05$b(WS8MfO0F9a)T3&LX`J15 ziVuJg3&gdpBP+R}8&{50B9j>PzT>yJu^wTg7D$W2+K9G@EDTO?eOPhwO*RupX@)TP zFgQ!gG`mA%&M))s1Qx$%ms9~gEF{d{0hTy7 zI%(lqv**8Wd{OZVn=C)Ec4pprbBdO|Fm1Y$vH9YsuNIzkjBIY{$}vt!qX4p8;HHti zF&0+$tWxcm6booz4Re&L9|-cosz=oGvC&l8NLUx)kQIMRE&5VJwBbT-(L{q6**Kc$Vq&X^jhz= z_BmD^sWIpwb-!Z#2GZ&-u8t#dN{>SI$JKe89+f#$n<_c%;*as>Z6v6e%Hkqmeh`u3wcpZW2!vYGzW{|k{Cfr@r8}%2c?HltUBh? zqx;D>SF2D8{nFvcnX)y~@II5wS)0lnhl(YQ%ia@>@Sga!ugfLFXbqPzCCf2%Jgae; zI)X%r_zb&>P${^dpY(XsEMG*)JBk}e-n$GXMckA;dT&+jso&OMOHtYIAF-V}Ekt*L zuFKZ>*LBeIK<7{H^)7+*?CwD(VD80NO)dMnM&3=W%Z~{4`_om1@2NeqP>ziD)We?C zj&WhfJq}MYRfKIEGYp7R^O@nHG(MZASdAbE#umM(P=cbIcX1IlZ(I?{F=&u+s?;%Q zvo8SX2kS?vKKmB0+>yKbhAkZ^B7S~PEm_-{Y}BiAG5T_(0xk6dV)J7;wEH2DUGzmu z=J-51bkZrne)?5L`8x4ByzhMFX;7kDXf;Ra*#2bT5EY{1j`}G}7HnfkKl!QZVJd}u ze3wF6PkWOS0*{RV*ETLGfb9(QtT8{GGBLUA8?lof7E%xMpkV^r%Jp~j7`aWuNUN^cTZ`vkV%+Sx(&Y_{)RYQ3Itn{s#o%@u z5;w^|`G_$RX&f~{p4nMXc|FHTAS|atD#%)n?qH%}Vq>~Pj8)xNi@#L~L8ZDmVVh@S z4>ME({G*&S)93@Avx=o{!ip>n%RtdXmFys>;4DsG;!My1Df^Iu+C3}Uq{nz{;@q{_ zMgmj|xdPo6Rz)M?szp^{I$JFEOijwRKK1oJIZY?6+VWLjL^x(OTx^B}3`1=McYG^G zNT`BdQ)zsbg0{qPi>U3# zz-mHzbvt?hXIorM2c%2UsngR5?0KxTg$w729?hV3danLP^>^p#VtPQ`xFW@*br2If z6ia2cfsA3{Pcl+QZLOSz*ySEkYhg(Tz)3D8XRLoZ)7A8Z;N=&DQPIEY5R>-z-4GVB#*Zum0DKR`1WBo_W0GaW!GKieMG+2*-7Cs#|zlwXcp_c@E4X}`I z{r|Rw4kv^teaMJ4R)|`j#RilbP8K~loDga>Z76A{L zjnOBG%kAYw2ch5HOb^d{N?Iv|9=f?|32n%;DYh}d8IjPw9{WwQo5K#7R8WMVF{3ln zDWz40J$Ae+f*ny~EhSAJ+@-HNwe@fX{I9-PX1UrkOKvc+1-8@}UN~fuJ*$Q5CkooR z7rjxeMrVHaNd8i)(|Sk}!eIhaJ=8maUX<|itYDs4yFx2B1dg;PEZ#G3n_r_=eHIR%!gy9?CJ@`^){D9n;cLImPAaL#-1TX7*v7_A3As% z`FCmulH==Yd?ttnS;|~bZ_US6AR!u;4n*mQZ1l7Wvnrz`idEZXn3?;MCGCzUX64ON zL;`Z$@aWRkvqDyBHlfz^%24t2j3H@D6oCmy|24MbhH{;UH%uol1%XR~t~hO(`}?xJO60dam8+ z-bhIy;d(-DRDbj@e-H0ou6#a%U@$bx-@H$RyGn8`<0YR4J&8Ry2?r8mTn%UY}c>sLZn!7=Ff+2~1ev7W=C}LbuXG+3;Qgp-fmuFhw@i z@vO+aAS*d>zu{;{^C6Z~`-QL#%VA3W@1>ao4~D_k1Ko5YCb16tgjm3aVnYB$+febmJ}u(sE2^wTMhvRabxFToJC(-`o8qfk6YGTPP? zlq)KTsG;4UKETt*Hh_BZSUo-AK-#82vF_EAuDIs0iUY@s-ZZ@vb;QNn1(35YsSY!; z2mvUtODAvtm&!`DCu~MuduMKT7G3BLYk3ZcVP%>iGuztI{|?I{#s-8Xy=CNj`y=$= zfi~b#GU}(mI-7#Yz*$j5-k>p_}odo3g! zC67v?t5&PS_#gWnWB8N#(&>RY5O(fEBn7iksT~)`n4g90cr@s|N7#ckyW}y*oRw$I1iByNT&8jGPBiL6naOswuo(X>1?4Cl!Pa*({d4F;lJhQ z-}CSn<_>SRAg`pc`VjN}j9-m_%~)hCwY z)%Ry0&SLc~sRBJ2kHUP}%eB$x(B0D!UM4xy0$yqL%h7MV9AH;rvBSku|M){EUajKTTX&+b3nD%-(lM7t z@Y+pT>@3%?q})xW)sJGM;>edaEy_(`~DXX}<8(lW1_JZ+?gcG~hA-T7^K%JwK&e+q1DZYg}?>JqmLACv0j z#X%BUdYoxiqtL};S%5YN|2{(}*SHX;sP?x ziWZ$Y>zF(0TM?Y&D2VF^J6dtMGg>?k44xEx&M1-+lbV^`M{Tn=4Zi04i=h%4pmH^q zPEoFZ=w#{S+C6L`rIi^c-%?{#UWN|l7&;=;dIDtHu(+VI1{B3cJplEjk?sz?wWxG8 z%$gNd3v0n4ni`5Iy0bAx-3CLqadhHr5-rB@J&qM4)#I!4J&;n%8#}D_O#SB{F_D+` zMi_-E8CLGO>Uk15a5bC2KK}-M2RP^s62tGl*ZL>k%1s?IY0q zpmc^O8JI{lEB%U;wN7}`Dw`~mPo5;s!pD0EA-z5~zmgA6J9%Rs`OAEmsN}I7_VsGX z=;}@CAm@@p+$%ZCQM~~akX)je zYym;meGQIKQcg#|5?J^*c#JG@QA-(EU4|W4Bd>nM1^Hgi0s$Q+)cL#lg00jQ^W^1^ zpJR#=(mT6m4S&p~rXHJA=vcq}vwIEMY+`gT#ny-x^vb$g@p!03i?b4r1V=_?xRgtM z+)dA`*+KB2)>h78d4A!dOs{9+y57RS z9OElql_Dzi3gRYZ)B^!h1_S z3-YVI>0Hwhz^x>Q+plTjvLUN$(WfKX^f>z%?el|=Po&m{wH4&vg^949 zJDCxqB|Ed;`P8=LR=awYFO@u*kBUr^s-C}&e>dBho=&h0sr$;|Wk7PsM6SJ?i#16U z6cmBH%BvfjK4fiHfAaJnu@hw2PwnN81G&&~Fgq;Mq9 zgtf*%?|Ym{5|yqk=k!S;I1OW!PB=`)FIs^NbVMyV;p6DbRyp$o^R{yzhkehI?FX3@ z%-FNCX(`a@917HSmWZr*)6QP1QQY& z9F&m=^xEOt5Mh5ep9Zfan5#N>g$;d`Szm^Qz0H6;98oWAT;Hf{)M0&aK{r z=m#6ERTed*7aMWmjMv_8=;*CV3^{S6h?J362iri1Bm?=z#FMmCB%{xl8rG z9_wGNeI}jyDHRZ$ z?qPf&yRqnsKit%*Y+jm;u;|x@Sz5+Wz(*T!qj9=KW=HzyA2D&+f>3OR`Cip0jO7@> zV^$t8dhutREtryVIH4&tsl?6)Y7|aeU#H;(ac2ZMJaW=j^Dqkhm|K4#vfhnI7!zGs zNw4deOaA{)dv6|<^#1?*ws*B_*Vl6IQh#Y>^Pr&(4qKd3bIw^rL3Ecn5?Tzp#nip6 zbf;-$Duwd^CZ%93zkED+rdqCjXVRN}BN=dQc%Irn$Y`JQvuS-*S#xh(!< z!5<$!pY?h_U(e^`=`BNGS3n}1URLW+y{B+M;AjX7((mv|0M`D5smaIcw2w_!*2>qm`xNASea-o-&6BJ2^EN<%;S7szvrTk!+8j z`?*FSUPxKIPrb2a;o#FOq6BNBDya;ZqTNv-nEvqqsUe8*X3kt89dTr><;j19`seoY zqq-16bNlMjIe$LfpvED)LhS=e)2bLjuw6F`_Jf;N?;8AZu(7J^32&#kB@#cHR+2!Y z3SxWI`|p8eZk!$@^t4#WiFKg~UWlq-`KYwl@*t2PP#KqmjEEFSt;dN1A-Kfh9)aN9 zTX0Z9b}IwQ4Y9Z@$6MY0O^O{D?ioCT9f3um(w_};QbJSAvUSZ2iLFhy+l4*~1CV9~ z&A+$vwFqAn3O|*%-GuJ2&g8ET_t;o;T)$2QeRt}!)68Mo?rd!qQU4GA ziczFoSNE`YVsT)IgIE}8+Z88zKJeRAT>&Ad+~ljq*@GJliKpulMLnxbq4uj(mibU< zT9SFIF$m%LW>4AuNB{gu0a)nZ=Jf^}Cvy;NmU0w*fOFc{|YXV3h|1=?J5 z8qI#WXDY64EO)o=M22#NI2MlE ztJ34vR#Z5(I~<{ADMM@1H{pAG{#T@gvyy70f(P@p+%d54M4#yu=l|6<=c_Lb>5 zTWsxAVs-7#);tYzibiIu*sf-6=~d4lN=)CaKow=UA_1mMl4AyGz^&0eiP*o_H0ZXK z3LMGwQcQjL=)&W3yi2kps!zb#9(LY{EElWeA zUSLZPMn#Mh%pqjNiA!mBWvkV=1L0{H`gNk`v2xSwRg2z-u#KTRA@JmE7TpN_Na>6nXb)?WxqM zkOR>Q)8f{aj7k8?!<6S&3lhx!-rUdxGJz%UMdw!IiX~>iKwdH(sP#&(W?*$t^Lf|}3HOr-)`TPB+xNZUY>9$Ufhvl{Ir)@;BBN2f*9 zIf35i^H_YG>*j^QN?^iS>5*d&gR_`i?-Wje99>EirJOIXEy?|Rjqk$Ma6VXeV=05AH)do@GbrhJe*5 zlLNxgX#2iFu)6xKU29T-?zcu7`_@xnj%?4({NmOA7S$55zSweL?yh(fFM;M1GfJ4m zyF3;}!LNc$d8c?5Bbp%i%ULg{(>x0`S+S*0u-wy^#%7<2Kf7A<0Gu*}R~?QPi&bpT zjTrZhWfAih74b$Bw*Y-kve(Ht{rfvR846KS<0VE|>*60&R%3HJegLwt76$wIPFSYI zyvmSGr{stBM>lEu6!-@br@St9jY3mi?EU^*R4irj4;13|p4t~82|d1v2q01-0#bZJ25u(#QK%r)tcP{j%*_G7Y`lO`o3;(JO%yH57-h zwV~v&uyw!C*x4P84BuzfZ$*s)@<)fjtsVvsfS@C|!(KBq?Vll7zCC3kyW_a(KmQRP zxJ21*p3Ot$i5+EJ@lqei$djITm&Q3%*YwA8vcV|!Ldm`F(xNU|@*J;FOYSe%h|DeS zEc2AMco+7!X!z=dF;FUzeO27&k``swk^N|*xL3eLbxUAc_g}K_qd#yRpKEj`?C-~d zsO}f|stua_KlYSW0U6U@+D0>F`TMZBETrnKc+eG{e|%Eed}hI>D=a*v5!|S$Ffpgx zzw)e|&&~-Sr@FAG1dbFK;_={2DpZ6m?+Kd!B11OysMrBRo*N6}{?vQ$U4LATr<1IQ z4Y@sOW&DPT^9$}ZHFF{|sZm%nGmSgWR(7UMG-MA_>M>t8gMfv4>M!bx!YGg<(T-t? z7Z5p3<${3Ed#?Vy#%1KBeo*zb<<_5$1K?ORQ&`uaoFhW> z)mwgNla)v7?ql)%D|fT%Z+3BKo18L)(|~mS_qQUW(Iq0Y>e-tOe0_hFnYwl`Oh(Zv z?kf%z(IY3?Ur?!pj>~i?C~MD+{C}5)`}dIthgs?j_I@w=?=>CTIbYp)-vixRa_bD5 z_NTaaq@8u5CHrEjFRF!}wk4@QgAp@_f#5%LS6snp5hU4vvksOM|}tw3*@t8l41PG@vB*b4+Uwq~*+=!tk)`7eL{x;kb4T)5U4*)n@MO zFKn*3U2;PRlts&9F_GFz%&l+%p(Gcu&B)j*Y}%x(pK5t)Fc@!!vy}+ZI8o8*+Mk!b zven;~yobjwi^r+TtJPQZqAO)%*yf;#qsp`+AEos*5K_54et00S<$NDL@ccy)x}h)r z%-eNt9E#K2&@hhk3&gQ*T-fWLEWm;dWkKWseNLj)EW0{dp5B+PT}m2t=^fQ)*80&z z_$8>?f@_ZQRzmZ0F<(PtTU*=sWz)Ma763OBvO(L0y3M4#I~flI+!pdbit&j+X)MU~ zGzr`I`;t`{V$S<3UiWiHkmM(+;S6fJ?0M{{D*j#DryAwjhzbxAe%iYJ?={A|layG3 z0ZxK>{y?dcx1cjZgtmgA`?MB*&2z=UbP5}4tyCSM*=gJDS`H$z43vYScc(!r1Ar2BXzOOPo>x z*tz_t2#=-UB{D_Bs060!)dhb0yEdia)1(G71F+C)M)f+4SVg`so+P#rbLs47-p-n% z2p0o0)`0O#i%559p>8_5r+nkJ`kQXG$u(Dm-vZIhwm8Jj_UVkL@%}1K8We8)?xSA( zSa9n|Hfbcs(#C#m@+t+IB+?J~MQ{E>{ypdkS>R*5U1m!-^OM=6g-vW)eaRN4YNRsu z79&z!yY^G1GXH+><5sdjtA0d+UGZ}IBaJ?x?LTU#BznyVakD}5&H<{N>rzQ_E=F9I zh_Vh+Ql5Qp^Kr~Pd}DsD@xt0*UFzqXs)bf{(-whaso8ahQrH?gHS=qqu{*D;i+Va(vl)@P&qef z0@{jS?E4w`a>lwnFLK$9ZQrSrVU_)NZyLWs9`H}Z0%@P}3o+{h_ZT~!)1I=Rn2B+B4>6n<}F7?-+IK}eL&VR8=h9{`BxIYyjb?q zYV|DUNAF*m{Jm$1-E+wEa*#(0U4CR4@#PaaCkS8H#1g+gU|fp^(^wKw$TL|*EMINm zBSl-Da!UMEL7W~rlo~S&Vg4V7q--C&}rqGn3$-+Q}p`1+M=UQ7Ch%CLB~6U z6_BZA6I@eH<87&-p=8+ZA5_K#f2o!>a<(sQbe_Vgw-wb*1smHsevK50(y z^>Dixo_tB2xiol+QP{%<30_lR1R7ajo<^SK0YKY63LOk9=H|E-d*=SX*K~F8mv$DB zKuZ*xPMh8jQtDcMFu-N-5QP#f*NNZ;Al_gQBO@ zd*Ak=@7C>;yJ-=Gd*A$ZE1&ZDHTtpGI>o_~cY*D|b5qm|dkV(Lu(GtC#nxB!u1?hz zisQwtH9ZG7+pJRv>x{$BuyTIbnruY7&S#(Rm0qcA9+qW?Db~|uD_Mg|z zY`d*bC^uWl;Z6iVSd6j3C`bmV%L^j{!_)5wGUb~ETkX+~$FixE8S1;nVy4~dA5@`Z zj}5+=(t#YR#?Z)w@{J`EA*dw-V((A)XQ+3K7S6w6-v=YRDwmf8@@+C!r6lWkJFq!; z!S-(Nov9)#M%*DT^=D7FK!R;SPu@m~h(lzT+O- z)cn2yLS2zl|B&UiX@9-jR6AoA2?zOFFZWgn+O97}AZP1XQ5B&J@YX(1m{$ad%z7y- zRZm?j>U_ z5_heqm2OOI_&Hn_dGq-DvXf(bZZu5!o&eOyr*7G@D2bh_TBk&XPpJT};3Y^*LCU^! z7wKu$K>(E`u#K)Bi8RcuDUDQ;qXi(LfGvrPZKS0ZKes&Bxv_Ar%%q!A+nr%Zym^d> z-Mt&I@70EV(6%~_eWcDiiQxX!A<5pyQugBj%kctNAAgsiV4%d&^51_w-vj7hb^4-o_H? z$EWvTeG~2(RH1k)d88VDx%ARR2?vT0WtltP-&4l2E29Y53jwL6sOGut*sK0zGH4K`hc#O<=t+3s)l|L zPk~4z*_WP+4$c+)>_ki#YM1KX)p`C2!cKC`zVCO09XAkey*gB0wA~(B96y&!SRFordhpX6g=gi%mjK!3E+p&q(z!t(&284l;TE z*3V<-yDP_>OkTP9>*7CMoAPx+i+pW$V*I!KD?xe!s*1t0s*LT-B6FyV5$%8)rrj`s z5h~Ya-V$j|`@9g)vmmHJ;R2@N13cdtu8kD64f2}x_nOZYGk4$J@E}8bw9mL4x?!cX zIj-IwSWu~%)goLx#+9boS;mKSRyCp}l%m7@>I6~ulBRO- z#XkSlbrnLUn?Hv7s#=7!W^?`d{Itzo8aAC961}n~mX-Ws*Ip{7vYxcHIR9+yACJ^! z(1~gg_x|#0aZTOymP$p273+Me^0u@CZ`D(6Zc&wGAX$jQA{RD9#A~m6+ndDQW$k-V za8N*;EZzF}$>SjyfE(m!u;S|d8$T#;50T5-_d@T3SwX!+A9N0tU3_S&-Fg9Te2DlE zM0-n0X?xA=rP@cd0T{M+gfaQ-!9?BdGrr4St4FYbZTWvrJ^}O>u7w0rwqRy*zDC=z z14{0ThZB)Gq@|?3Y4^QKy9^tQXz89_;;W>jGw90FgC4I|azaRpxtCdSy4F?j7MM&u+&()Jdb31CeEiP|fTFegD*HmndKe=O=IF;1wiuk$mF~73^a{WZ7|3(L==*o&29A#}ZFh%u#Mk9s zeze7}l9~?^93&Wm9(zGagdP#QEKiFm5Cnb$&OGT_uRL67m0=ReG=jHUc($U0Z?te! z>VcawjZNDbrx5()X`*#zK4<@f$ zMENr6sz1mhQ+cMi+fK#T4AS@W$+j2!vKCE*j1BRwM^GsIGMVq#bJ#}50J|*aynsby zUH<2^lh&suP1AI2=gdP$8dc0z+muW6<>k7Frj%L#%uXfY=tXI5#>wG^q~eRnEui7zt_|%r@jdtRxzmI zGR6=o1)d$)rjx^V|2efj=-{bM9LORfPvVzH<57>r{jtt|^5Y7qE&fN&{g0ruA2}(3 zf3FD}XNm06t_fufw`;1B8hzSlf5;#b7Zgq|`Hf=aycy&QbN&mP;o!f{GNRY6@{qI7gb9JjV*7q7H!>tO8q(uW6)GlFR>SbdTK@`S8L7g!9Jjk3A* z@zQ|lxbg;21nktQq1Dp71CTGp962~6KLTZ45nI}i+)pZX%xq?ho$W~r1vhi( zi^b(q@0TUkBi+Sn?-Rck>>{1YUKyIb#{rIiRX84}nP?(Z?uCba$C(0bLu)iC|{VR%5JvYi-*dMywC#)3LC8h@7v$DIv7x&l#QV?0Y zb`Z6+eudGY4ss^RH&t)#R!V31X|U-Bemda(Wpo9Vl7`w#ii<8^uY}ff?DvZzE(#7yp zw0a;+6p=mTDNkf%Cq?M=H#&Dq~QFP4(ALxns2E_b&4(y;T) z*Y7tfm_Dhs-Y;$yZ1AhyX4vcS<@&jA&y;EeOa_XKTosBPU46xX+PZYz4dHVd=tR2a zQrEoi9sS)f0Z6_5pyeObu6g|gFOa#33%9@U)4}}jf)cRsw|}oWr@R{P)}eiAz{4my z9Q3;sytR}2Z^p7L+u=&P^+T1F?Dr*|sx2XMR`zdQl0CEn1|C=XP+#m2`gYHa=D!Ml z8Tw`wJ=^DFs6K47Gto;TCAOTc<1fDbDUO{X$6)L34?o26)XA-ZCY@Aq{AnVa3*rSkjF6y2O-N6nD9_l6RB*Wvk28^ADZ z9_%+mPa_Wnu>U{^u!)dj`I5Ke=2CW5LxJhmtHU*_$(G=%RRarSv)nR!YVJ5zIU}gv zA3k69_ZnV_=*zRA6;Ly_k1w`KN~uFks9O=(TcL8gs6HNyD$un8s;c3ltm@i7%%jNN zFSC0=QP3BA-i%rL0eu_YOTJW0Ws52yvT({@ORwUR7w*>T8IfAx-P)b=3Nx5P5LCGJ z7aeMr^EQnXIte1}Lg0&sh}!2>O#(w+P#6wLjXPlMquy50cM|jVbIdn$Omdr7FISmS zyG&C#h`7&f9muk{Vu>LHJyF-B+I%OHZP-XPHNymFRTWT%1Bd^`x!JNnXy7Wo9*gjW zU8F9XJS{k2yL4J24nPLrYurT`1t3oMMy{=JxR4sKd7H5E zhp*=aVvF3OD!rL!m;bQ|-49*4z#QM1KyT_6R&JkMW2%PR(5MwZZ!BIdS>%%F{ZGx_ zUl%63jWg!m-ZU)2KMj)=&)nKMC&JRFf|`T18Mz7tsi7xrZ=mP1=eWS}?ArWpR}`Uv z=*=g;o>v|TUuY%xuRGyh^enDhWVyo)wCv|(nDJ(OR{^fabq^Di17}nt5II`la{WVG z*UY=mg2HlI=OaeAR9DsgfmR@PSz+_T*eZ1!gYi<-4eOFTD%e=LJ5N-5>hs7qOsq|s z5p=QAATY%5WC;Q;t!pAhC~Ff1hfkynZysyS5|%oiCU^3~!Qax7$Nj=BFhRy%6!j!! zS`V8d;DY9#$de^2ahSDO@KLtMhf+`&DEaszD?5Ro92Vbt6lSPfuh>a!%D(b+I`~0& zs|K6w`34TfDg(27UZ+z2-23s3dC!R?`icDZ=11}OyF=}BI+)>39vQSAW&qy?RIz<) zB&f&RJ0f~Fw(5?Qj5~S{dXd&RwdYaOM(KY(l>R>qNB>?~{g)`M|I69*)5!GG>9+$C z3A8pw8$XgBu@K)f0hx)r>hvaPWOsO~=JI)kqCvFtpF(o`)C&Kt*Q>#gg~cL|3laLG zYS6(g6;S(^OcO$!gIlyJR-yQQLM(a26O#Zu*`x%u$ofrxTJkr~ zBz_I@{Vi`4U-#KlGgMyp@M5N!)a&|bV~_g(t`Yl8;uUb-4#?(rRDo*;X(fRN$bAV{ z+)!lJO#2#a3|yTfIKCwt*YZ(X^*E6YLoetu#f*cElyqwESDA7Sh}DF|rldR6dcoUT ztbQxl-;CR}AwQ+dKSIXJ$@(Uo51!`ge~9kxdezhK1F?ujHI_hOGdi4qD^=@2nR~=OGz9FJ*iUf2A4WP-6!7dw1X|%O^fBDgEuP6Tlnf!m| zV*X3A@!m!ZVa)dkRy9?KpQ?Ar*f~5lJ7bfS8jM78?_a5uNP?qL8I`@GV-;VEH=A~k zn!g(@QnhEXb9|bF4vCRgRc${^VyS>`(fL&Cqkelz33=wQeKVX8?Z6AUdxMRgbVEy7 zPU zI>)Q*ISINzK?X2G3LN83Tu{&6#{MaBb)LxPHqzB|{Y3Pn1E@LfA8D#wr*C9D)OGCQ zn93XuzbOU(hLxn##|+a?lM>$IRtG#jHDEAaa&e}bwM3d3zAubyT^XMx#1F>9E$&`q zV#MRmCHN-J=XT8OLpGr>4BE;7sg#!^{vjAUxv+gZo!)NjbJ%aia{h4RYIy%kn_6w3 zPx;h}4@Y5k64osmC)lJqPUjde!)y#f|2Dax75wEw@-iY z^w7L6eI}189j;*gpB) zgolo45BBM<^i71pgZQ736!MD@_VDMB;qQEWDExpJ;E86&@NyM>uDkh4Q9u+XX zqbwW0^^P_Oz&|7Ve+How%5H%o(F;%f6>vWItbJB zIirasnFU zblyx~lqu6sSRK_xmyWk)JCJ*x5tdfk3U~mvE-=tLBk6X*&aA-N1M_Chc`;5yWu7U| zQmL*ep;>NnyR8{zY9U6=xuDkgK;noBvr~*!+M_s3CP%Ele(|IYwkTXqQT@H3O z)ABC7Je?VC?87At<5>v)*8TLX;N|+&Pxf4!c#+y?b7~O6%0L5(by4m z@aO&>pugtq7yOtoq+17~_(R&O$Ygb?_qIyi0pl0Is&B5^&%$~q#-uZJ+BO>IJn6(s zdJwI@!OykQ?Pzn_&s}Y4)joR&#J(`JgxJZeCLujKG0vX=bdvE3-x6sIeat?=<#tNS z`cEDk`=8Yl@~RgBpAM=mNgUWbC3vPTU_PGUnty33uTU@tLsS5vVX4-+NkqP+JKgT< z_BdKrCH!Q`mxf0Z=~?DH_+?tJb&PYq_o`O@&;JzQ8F^A<_{KL=w&9G(anW=~#Nb_s z)w7G!MG?Jk#IagQY|jIU()tpOzq2i>i`M#JelWNbk5?yJo1Yv^dBjK{w?1$}X1-de zu-bE7);?Lt+>aHAT^NdIh6aO$CDLl%eDNEs0Kd7FQ?bZEedqupfY#w0UR=|t2^(Kr zuy9dmerOIF%tLhpyw0)hy`s}5>CWjOzUJHf#*^r~pFS#!nh{2?c$VBg`&Nu*x{i5p zf_+`Vbi;r~YIpvn+VY9Li4xjAZORstX%rFt6+}&;YOz-YVX0|9*f=JRMYpLkz&Q1b zJyW8NGjcaf+fa4LQ6~}6#;;px*~!acBskt7!BTWxRduTRs& z5A}t`lMo`apkJ&39Y>Q|1>>oPK>Wws9$V|&H>_5(240yi7PTxAM5I7_%oXBP|9(ai zZf2oXo*i9;A-E*%DLJ_%N2NspXP#M>{tn zSN5W}@sm9vaZ5ztuOBqGa5VTPF^#(U@$ zC*WdbqS)S1m(>qbBuLLIxciaCB_dj_RVplP0T3y+K8I zB|0C;9tvWd-(X)Ti#qGf{y)Vz^s2daBU|Z&?ADpdF!%vSZYR z4LMS%vq-bD2spC(t z^LBklG`8t z#yTYyBlU?VF(uG9Ecu-{kIi#7Y*%G{rR(uAbaErhsrlH-Nj`FQj=O<%1~F;y&6QfT z&Ztuu-aj-2AywA)M(OQl&b8^}z6mReEw)&LdNg4p*+i*Dc!$5(v-pA*IK!#UcIOWJ zAm8%NQokGGo$_j?LxBTPX-24grn1Bj>JVrj)g}Uc4e~&@Jd~|$c5VJr`J_#1?P|21 z#dP25H)L>aPRGc%%_q}nG0I^ms<7i89-&$PD#)2P5oOqou~(OdX48??cu08p9O5B- z0;cYBc?WNY6cmeEZL6L>Qkw|j1a{<6bQ(FWj95|dkKBXkTQ4SVWZNcx2-#!DOT6j} zon`igVA=7uW=0_f_cmK(pHIhRoNy{>@7U6h6+_gZ*nPx>RvX3^7vh`d?og&zQnw61 zzhFF$?f(Xwx1|hUOx@iOtH_Uloy2TyK_fx{KaG^#T9jmd$T)?uJ!X||@AwC|HW}62 zJ&6F;2ZT+);Wl5oZ5%-3RI^P?G5`nkY^b>HDI1$Slk0-s&xH@#EdEHG6#`Vekjs*B zo?;72WLBHQOGhmH@bTK_=Nv`-35F{uy+OpT>I0OiiifJGU%uqDQB9W+JC0rQ*!dJg zV7!J)i3o0>7zA*!V8SG9TC;foG>vlshYmnMBAm3ASs>&~4S)B?wWN+}| z#Wf}MDFB(Xaq!KWU8|hZD2IPGXTJ3a=M9yQ#5tRJAcXkhn+8L@6m()8ScvnMAJhGA zFyWJd_dnF#oZeqks#tGn=M-(9>x%lQgr%M-94#qGouRZU=S-yy&yKQ{rKd%*z&MFl zI`VTO4Jr2|BR6^jdEekjN2+TAH|}D*Zp59PKRtJZ%A`6quuGI7)nK=P1)`{lVf|60 z0(=K;Yi+f4;CVFBbaV~a`Ml7{GJSTfMK6rj)k;ifgF@j! z9@NFI);2~yaPj@H5h_t??I_6=Z0t) zv8pT@o`k)jR6{46oJO4UIzGsMpmZYNho7R-rG3t&(`Qp_G41F|c7Ld}1GHTq9}0SN zABv`nAA5TI`1hI+%MHUff%ytiPiskSNA<3=n1hLs8bP0ju4vdW_~7i&c;N;2CW_Nl zWV-;n`>z|2ux!40+Tay0YtSB6p7Flg1I8$o>}}ZuWIHSiY2na4*YYX-Mfrh!D0Lp*z9U@k&~@*K5_}KLNt$2q5yytsZpalf zDIk+a%vY`sQx+Q4(k>2Bl+w$OR#~MY7`V;wNal0H{N6*T`s@Fs7$w!FYXL9w`W90O z*;zap=@dP25EuK@zzyz#2)p!v@ivrX*4 zo6MqV%O1^URu+kvAJ-w+(3v8V&21nGPT+*4{61}!CZJehDCR1L9Mzgl$y0n^AAcMZ zZ{H#LAT(Hnd}$`(3)nK+$Lsw5jAhsIvf|2(+4e%s|c0HWhr;=4pe3_`^D_Iu1Z+G@z%Bv1m;w@mslW)8- zNQeW*@M$w^bcc%p)9w}kC+;SX?fh%%`J<`yheRU8ysec#{!f8p(7{$i*?@Qg!IaZs zYKzqLlPcqP<%Di}fno2x_%h_0ncs4|XuMI8e0-~eo~-EKs}uGC3fqfp7w3stu& zgOokq-J(F`R0g1fxqZGF$E>sxd0$FfA!@yCni@Mu=ZVEy?yJV(jeF0kqQ#F;k@JgquFW`c6rkPtWNK=Dh0E0yyw4exii;&)fw`Sg zy$r08&=Emq9-6|KJT>l)!@4N5LeUFJN}%QiEf$1+Pt*W2xg;mgQrE!17CX(MJ5&NY z6M; zMqa(b5uoe-T?DR+Tkxv5@`|6-ywve3FAA=)5A)3#lRo(;p52{7v$LTVwW?H_ubY6< zO(bRPo@=*S@(Z(qckPAuL5HhN2CK()D&IKB4gru!-qPmOm^*#TZ5HcxM=K0V!5byC zMud2^Xff8*PDZL`TY?}{UEG!u+iQmav{+Ex!|5_K_XSup>R~TkJX4?HQ$9!c|9Dp59 z+1+GUE4#nUo351#LR#D97TW4_eU1d3yKB9mGR^xCL9x4CSk~9TI!II>8oMO6`?^WJ z;>{@#4=@fGPZ}Itf|t2lxm)nq7YS#cFF+E{l+5SHB4Xua)))Hc289mN9{bk=d|eKke%k*dB+=2Vo2qD~3ho zgjAkB=#kvEdO@J|_04+}7b~m`5+H*MX?GHWti#wkjk$^Z?CkX^v7#*{eLT}YewvpqLx!SnH$p`Ht@ z3M6;v%V<*q5nkyI~?V8gJaU zUpM!7bF&6z=x0`A0a>C+YptaVhQPfyKC5glhZ(YV6}wKN;IOpqjlh8j_3o$?D6iJ) zk-9#}T72|jTzf^<;N!NuN_#7)!Dx79%J-B2{x*ea&48^bMgd^B$ zg~-z0YEBnb2Z>=-5AmFNL4icpW}(U9L-rNHY!dJeeNkdDn-f*6LuGW5cQNp zwy5(TF)#74+d^b>SoVBh2m#HlIMhn zQiKCO@FYwql~rL;(P=r)hRaHeFLZ>)cSw4!Plt&kyD6nl7Gd!*NQ`P)wJTZK6WAk2@Tp;xilaw$u0VG?}wDtD%OerWR>p{ncfgo>JzG6zVO+O z0Sg6&I<$-mJosMx%s2O-W=vGe*hbo_N(@)8$2am$`P0_7_4)PLDx=fVx5{3htNL^A zz$Xu*qT9O+QZF&+vXFGb$%~*mW=^5lTo$1uH6oFjRY^exP~Z77y;CTJLh`2=5IM{w zaYnYo#;#hkYLJnx6HH!^NPBKs%=8ng^psY0uFKuABC@nDo-y<_Tgb*vV?5E&qsN*i zo;g=i9`eUadsm-kMF=1X>o-9H+& zuDJjlmq$P^XTH{e@CCF2 zLHy+=%ATV7Ol5NU*E}Nn7WnTq@*y?Yoqh{oyIf-~Fdzt~O8a8Cn{_WKe~n#v@(>Dd z`-q+8k#ySvgjf%1kjLd5|H(A#bce&ObyTv>Z!bf~{KQn=?I=y;YO;n)ihy>*&7*9u z==mqpF)xEJr45G~f*|)4CeH^jlAU)HJfHGBd=GfOBND8XVY~&whK2i34B76hdHyr! z1IB$QaPzDmD%l^cX-ao6r&|7MI9iS4r+xPwb;=0~B#braEcb^_b@@eW(3(+DD5FV@ zMj)nfkfI=_pUgyW`uCd5uCabZE3%w(v%oAD)u_!sWtT*mk{qXta4|DM7*Yl>Fj@)l z^$5W{P{B^M9xt8ctvVo-duCYJ`jA%u8>b=~qNaZOR4*Cb z!`(E2nJ<*(5=LvrT1QV4O11$zN8&Rwuak2yib9P?V9?pIb*{6C^M~*DzRRf+#qy@a zI5DWVMzN*Za3j&o;7AjsVf(JVTMR1#rCL`yo#(u~Oi0ZbjVv}VY@<~blVt~`b?h-H z*z@zcy|qc`gD*HN^l$-vA#vh-s!_ra;M|I#6K4>(c(`M(hBSTaFy<fMvTo=x@}355^%%=eJaWqaS1-6qVr5CRb-_dLbOHHE#6#?x^3IHHOZr{QvH5_-jm zv@c`}4N;lEqLVbtY7Bz>*OT4Y+KkZg)Y=wGs>$|Q`_Lyf=cbKu_b^QS0RYLEPB>k# z*P?mu>iR)qA4;Ig#)<3g*>{zaCN>Xrc?L(!1#|OaXJCh4_9&WKgqhA!U5Z1^{9`3J z*Tt)ib~KtWbJtq34#55=nmlJBfrGm1{+fYo+=u*+iBbPcsraY0;;l~>?#Jp?NlKAr zuqzss+|*|XP9DwLcEIa_jFKX-|eR#iy~3m#kdi_L$Vy|QmzcH1XMuwC|j zA?(gD(@ECCHZw$PHn8he@l90gX1Xh5B6$Hamm#3zho#eDtz{|Ei$NXnd|;|Pj0?@( zhewu;_h85?Xq-iV22?meO^Rh+Nu6&r==M<=!l4>w9qAxTWEa;O z3C2c8!yfPe2d#}7@z~7j)Ru!A13cDU{Quy({nzK1|2Oaa|Nqzjqa7OmjjNge_nZF% D(mg57 diff --git a/docs/source/tutorial/stable_diffusion_lora/stable_diffusion_lora.ipynb b/docs/source/tutorial/stable_diffusion_lora/stable_diffusion_lora.ipynb deleted file mode 100644 index 679a203..0000000 --- a/docs/source/tutorial/stable_diffusion_lora/stable_diffusion_lora.ipynb +++ /dev/null @@ -1,750 +0,0 @@ -{ - "cells": [ - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# StableDiffusion模型LoRA微调\n", - "\n", - "[StableDiffusion](https://huggingface.co/runwayml/stable-diffusion-v1-5)是由StabilityAI、CompVis与Runway合作开发并开源的文本生成图像的模型。他可以直接用于文本生成图像的任务,也可以作为基础模型进行微调,从而从数据集上学习到新的风格,或是用于完成新的任务。本文将介绍通过在PAI完成LoRA微调StableDiffusion模型。\n", - "\n", - "## 背景介绍\n", - "\n", - "[LoRA(Low-Rank Adaption of Large Language Model)](https://arxiv.org/abs/2106.09685)是由微软提出的高效微调大语言模型的方法,他通过冻结原始模型参数,在模型上新增低秩矩阵作为可训练参数的方式微调模型。研究者发现,通过在Transformer块的Attention层上添加LoRA低秩矩阵对模型进行微调,能够获得与全参数微调水平相近的模型。相比于全参数的微调,LoRA有以下优点:\n", - "\n", - "- 训练的参数量小,计算资源消耗低,训练速度更快。\n", - " \n", - "- 对于计算资源/显存的要求更低,支持用户在消费级/中低端的GPU卡对大模型进行微调。\n", - "\n", - "- 冻结了原始模型参数,在训练过程中不容易发生灾难性遗忘。\n", - "\n", - "- 产出的模型较小,存储的成本较低,仅需推理时和原始的模型一同使用进行推理。\n", - "\n", - "后续有开发者,将其引入到[StableDiffsion模型的微调](https://github.com/cloneofsimo/lora)中,取得了不错的效果。HuggingFace提供的[Diffusers库](https://github.com/huggingface/diffusers)支持用户使用扩散模型进行训练或是推理,他支持用户使用LoRA微调扩散模型,并提供了相应的训练代码,支持[文生图](https://github.com/huggingface/diffusers/blob/main/examples/text_to_image/train_text_to_image_lora.py),以及[DreamBooth](https://github.com/huggingface/diffusers/blob/main/examples/dreambooth/train_dreambooth_lora.py)的LoRA训练。\n", - "\n", - "当前示例,我们将基于[Diffusers库提供的训练代码和文档](https://huggingface.co/docs/diffusers/training/overview),在PAI完成StableDiffusion v1.5模型的LoRA微调训练。\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "## 准备工作\n", - "\n", - "### 安装PAI Python SDK\n", - "\n", - "安装PAI Python SDK,用于提交训练任务到PAI。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# 安装PAI Python SDK\n", - "!python -m pip install --upgrade alipai" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "SDK需要配置访问阿里云服务需要的AccessKey,以及当前使用的工作空间和OSS Bucket。在PAI SDK安装之后,通过在**命令行终端** 中执行以下命令,按照引导配置密钥、工作空间等信息。\n", - "\n", - "\n", - "```shell\n", - "\n", - "# 以下命令,请在 命令行终端 中执行.\n", - "\n", - "python -m pai.toolkit.config\n", - "\n", - "```\n", - "\n", - "我们可以执行以下代码,验证配置是否成功。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import pai\n", - "from pai.session import get_default_session\n", - "\n", - "print(pai.__version__)\n", - "\n", - "sess = get_default_session()\n", - "\n", - "# 配置成功之后,我们可以拿到工作空间的信息\n", - "assert sess.workspace_name is not None\n", - "assert sess.oss_bucket is not None" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 获取PAI提供的StableDiffusion模型\n", - "\n", - "PAI的公共模型仓库提供了StableDiffusion v1.5模型,用户可以通过以下代码获取模型的信息,用于后续的微调训练。\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pai.session import get_default_session\n", - "from pai.libs.alibabacloud_aiworkspace20210204.models import ListModelsRequest\n", - "\n", - "sess = get_default_session()\n", - "\n", - "# 获取PAI提供的StableDiffusion模型信息\n", - "resp = sess._acs_workspace_client.list_models(\n", - " request=ListModelsRequest(\n", - " provider=\"pai\",\n", - " model_name=\"stable_diffusion_v1.5\",\n", - " )\n", - ")\n", - "model = resp.body.models[0].latest_version\n", - "\n", - "# StableDiffusion 模型的OSS URI(公共读)\n", - "print(f\"StableDiffusion ModelUri: {model.uri}\")" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## LoRA TextToImage微调训练\n", - "\n", - "通过LoRA训练StableDiffusion模型,可以快速,低成本地获得一个能够生成指定风格的模型。在以下示例中,我们将使用一个Demo的图像文本数据集,对StableDiffusion模型进行LoRA微调。\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 准备训练数据\n", - "\n", - "当前示例准备了一个简单的文本图片数据集在`train-data`目录下,包含训练的图片以及相应的标注文件(`metadata.jsonl`)。\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "\n", - "!ls -lh train-data/\n", - "!cat train-data/metadata.jsonl" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "我们需要将数据上传到OSS Bucket上,供训练作业使用。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pai.common.oss_utils import upload\n", - "\n", - "train_data_uri = upload(\"./train-data/\", \"stable_diffusion_demo/text2image/train-data/\")\n", - "print(train_data_uri)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Diffuerser提供的训练脚本默认使用[ImageFolder](https://huggingface.co/docs/datasets/en/image_dataset#imagefolder)格式的数据集,用户可以参考以上的格式准备数据,更加详细的介绍可以见HuggingFace datasets的[ImageFolder数据集文档](https://huggingface.co/docs/datasets/en/image_dataset#imagefolder)。" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "### 准备训练作业脚本\n", - "\n", - "我们将使用Diffusers库提供的[训练作业脚本(train_text_to_image_lora.py)](https://github.com/huggingface/diffusers/blob/v0.17.1/examples/text_to_image/train_text_to_image_lora.py)完成LoRA训练。执行以下代码,我们将代码下载到本地,用于后续提交训练任务。\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!mkdir -p train_lora\n", - "\n", - "# code source: https://github.com/huggingface/diffusers/blob/v0.17.1/examples/text_to_image/train_text_to_image_lora.py\n", - "!wget -P train_lora https://raw.githubusercontent.com/huggingface/diffusers/v0.17.1/examples/text_to_image/train_text_to_image_lora.py" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "我们提交的训练作业将使用PAI提供的PyTorch 1.12的GPU镜像运行,我们需要准备一个`requirements.txt`文件在训练代码目录下,以安装一些额外的依赖包。\n", - "\n", - "训练脚本目录提交到PAI上执行训练时,目录下的`requirements.txt`文件将被安装到作业环境中。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%%writefile train_lora/requirements.txt\n", - "\n", - "diffusers>=0.17.1\n", - "\n", - "# source: https://github.com/huggingface/diffusers/blob/v0.17.1/examples/text_to_image/requirements.txt\n", - "accelerate>=0.16.0,<=0.18.0\n", - "torchvision\n", - "transformers>=4.25.1,<5.0.0\n", - "datasets\n", - "ftfy\n", - "tensorboard\n", - "Jinja2" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 提交训练作业\n", - "\n", - "Diffuers提供的训练脚本,需要使用`Accelerate`工具启动,并通过命令行参数的方式,传递超参,预训练模型路径/ID,以及训练数据集地址。PAI的训练作业,支持通过环境变量的方式获取输入输出的数据/模型路径,以及训练作业超参。以下脚本中,我们通过环境变量的方式,传递超参、输入输出路径给到训练脚本。\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pai.image import retrieve\n", - "\n", - "# 使用PAI提供的PyTorch 1.12 GPU镜像\n", - "image_uri = retrieve(\n", - " \"PyTorch\",\n", - " \"1.12\",\n", - " accelerator_type=\"GPU\",\n", - ").image_uri\n", - "\n", - "print(image_uri)\n", - "\n", - "\n", - "# 训练作业启动命令,通过环境变量的方式获取:\n", - "# a)输入输出的模型/数据路径\n", - "# b)训练任务的超参数\n", - "command = \"\"\"accelerate launch --mixed_precision=\"fp16\" train_text_to_image_lora.py \\\n", - " --pretrained_model_name_or_path=$PAI_INPUT_PRETRAINED_MODEL \\\n", - " --train_data_dir=$PAI_INPUT_TRAIN_DATA \\\n", - " --output_dir=$PAI_OUTPUT_MODEL \\\n", - " --logging_dir=$PAI_OUTPUT_TENSORBOARD \\\n", - " --dataloader_num_workers=8 \\\n", - " --resolution=512 --center_crop --random_flip \\\n", - " --train_batch_size=$PAI_HPS_TRAIN_BATCH_SIZE \\\n", - " --gradient_accumulation_steps=$PAI_HPS_GRADIENT_ACCUMULATION_STEPS \\\n", - " --max_train_steps=$PAI_HPS_MAX_TRAIN_STEPS \\\n", - " --learning_rate=$PAI_HPS_LEARNING_RATE \\\n", - " --checkpointing_steps=$PAI_HPS_CHECKPOINTING_STEPS \\\n", - " --max_grad_norm=1 \\\n", - " --lr_scheduler=\"cosine\" --lr_warmup_steps=0 \\\n", - " --validation_prompt=\"$PAI_HPS_VALIDATION_PROMPT\" \\\n", - " --validation_epochs=$PAI_HPS_VALIDATION_EPOCHS \\\n", - " --seed=$PAI_HPS_SEED\"\"\"\n", - "\n", - "\n", - "# 训练作业超参\n", - "hps = {\n", - " \"validation_prompt\": \"a photo of cat in a bucket\", # 验证模型的Prompt\n", - " \"validation_epochs\": 1, # 每隔50个epoch验证一次\n", - " \"max_train_steps\": 10, # 最大训练步数\n", - " \"learning_rate\": 1e-4, # 学习率\n", - " \"train_batch_size\": 2, # 训练batch size\n", - " \"gradient_accumulation_steps\": 1, # 梯度累积步数\n", - " \"checkpointing_steps\": 5, # 每隔100个step保存一次模型\n", - " \"seed\": 1337, # 随机种子\n", - "}" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "以下代码中,我们使用`Estimator`类,指定训练作业使用的镜像,训练作业超参,输入数据路径等,将LoRA训练作业提交到PAI执行。\n", - "\n", - "对于使用SDK提交训练作业的详细介绍,用户可以参考文档: [提交训练作业](https://help.aliyun.com/document_detail/2261505.html)。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pai.estimator import Estimator\n", - "from pai.image import retrieve\n", - "\n", - "# 使用PAI提供的PyTorch 1.12 GPU镜像\n", - "image_uri = retrieve(\n", - " \"PyTorch\",\n", - " \"1.12\",\n", - " accelerator_type=\"GPU\",\n", - ").image_uri\n", - "\n", - "print(image_uri)\n", - "\n", - "\n", - "# 训练作业启动命令,通过环境变量的方式获取:\n", - "# a)输入输出的模型/数据路径\n", - "# b)训练任务的超参数\n", - "\n", - "command = \"\"\"accelerate launch --mixed_precision=\"fp16\" train_text_to_image_lora.py \\\n", - " --pretrained_model_name_or_path=$PAI_INPUT_PRETRAINED_MODEL \\\n", - " --train_data_dir=$PAI_INPUT_TRAIN_DATA \\\n", - " --output_dir=$PAI_OUTPUT_MODEL \\\n", - " --logging_dir=$PAI_OUTPUT_TENSORBOARD \\\n", - " --dataloader_num_workers=8 \\\n", - " --resolution=512 --center_crop --random_flip \\\n", - " --train_batch_size=$PAI_HPS_TRAIN_BATCH_SIZE \\\n", - " --gradient_accumulation_steps=$PAI_HPS_GRADIENT_ACCUMULATION_STEPS \\\n", - " --max_train_steps=$PAI_HPS_MAX_TRAIN_STEPS \\\n", - " --learning_rate=$PAI_HPS_LEARNING_RATE \\\n", - " --checkpointing_steps=$PAI_HPS_CHECKPOINTING_STEPS \\\n", - " --max_grad_norm=1 \\\n", - " --lr_scheduler=\"cosine\" --lr_warmup_steps=0 \\\n", - " --validation_prompt=\"$PAI_HPS_VALIDATION_PROMPT\" \\\n", - " --validation_epochs=$PAI_HPS_VALIDATION_EPOCHS \\\n", - " --seed=$PAI_HPS_SEED\"\"\"\n", - "\n", - "\n", - "# 训练作业超参\n", - "hps = {\n", - " \"validation_prompt\": \"a photo of cat in a bucket\", # 验证模型的Prompt\n", - " \"validation_epochs\": 1, # 每隔50个epoch验证一次\n", - " \"max_train_steps\": 10, # 最大训练步数\n", - " \"learning_rate\": 1e-4, # 学习率\n", - " \"train_batch_size\": 2, # 训练batch size\n", - " \"gradient_accumulation_steps\": 1, # 梯度累积步数\n", - " \"checkpointing_steps\": 5, # 每隔100个step保存一次模型\n", - " \"seed\": 1337, # 随机种子\n", - "}\n", - "\n", - "\n", - "est = Estimator(\n", - " image_uri=image_uri, # 训练作业使用的镜像\n", - " source_dir=\"train_lora\", # 训练代码路径,代码会被上传,并准备到训练作业环境中\n", - " command=command, # 训练任务启动命令\n", - " instance_type=\"ecs.gn6i-c4g1.xlarge\", # 4 vCPU, 16 GiB 内存, 1 x NVIDIA T4 GPU\n", - " base_job_name=\"sd_lora_t2i_\", # 训练作业名称前缀\n", - " hyperparameters=hps, # 作业超参,训练命令和脚本可以通过 `PAI_HPS_{HP_NAME_UPPER_CASE}` 环境变量,或是读取`/ml/input/config/hpyerparameters.json`文件获取\n", - ")" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "使用`inputs`参数指定准备到训练作业环境的模型和数据,提交训练作业。 \n", - "\n", - "`inputs`参数是一个字典,Key是输入的名称,Value是输入数据的存储路径(例如OSS URI)。相应的数据会被准备到作业执行环境中(通过挂载的方式),训练作业脚本,能够通过环境变量`PAI_INPUT_{KeyUpperCase}`获取到输入数据的路径,通过读取本地文件的方式读取预训练模型和数据。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(\"Input PreTrainedModel: \", model.uri)\n", - "print(\"Input TrainData: \", train_data_uri)\n", - "\n", - "\n", - "# 提交训练作业\n", - "est.fit(\n", - " inputs={\n", - " \"pretrained_model\": model.uri,\n", - " \"train_data\": train_data_uri,\n", - " },\n", - " wait=True,\n", - ")" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "在启动命令中,我们使用`--output_dir=$PAI_OUTPUT_MODEL`,让训练脚本将模型写出到指定的输出目录中。对应的模型数据会被保存到用户的OSS Bucket中,我们可以通过`est.model_data()`获得输出的模型的OSS URI。\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "from pai.common.oss_utils import download\n", - "\n", - "print(\"OutputModel Path: \", est.model_data())\n", - "lora_weight_uri = os.path.join(est.model_data(), \"pytorch_lora_weight.bin\")\n", - "lora_model_path = download(oss_path=lora_weight_uri, local_path=\"./lora_model/\")" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "以上训练获得LoRA模型,可以使用diffuser的推理pipeline加载使用:\n", - "\n", - "```python\n", - "# StableDiffusionPipeline加载LoRA模型\n", - "\n", - "\n", - "import torch\n", - "from diffusers import StableDiffusionPipeline\n", - "\n", - "# 加载基础模型\n", - "model_id_or_path = \"\"\n", - "pipe = StableDiffusionPipeline.from_pretrained(model_id_or_path, torch_dtype=torch.float16)\n", - "\n", - "# 加载LoRA模型\n", - "pipe.unet.load_attn_procs(lora_model_path)\n", - "\n", - "# 使用推理pipeline\n", - "image = pipe(\n", - " \"A pokemon with blue eyes.\", num_inference_steps=25, guidance_scale=7.5,\n", - " cross_attention_kwargs={\"scale\": 0.5},\n", - ").images[0]\n", - "\n", - "\n", - "```\n", - "\n", - "或则用户也可以将其转为safetensor格式,在StableDiffusiuson WebUI中使用。\n", - "\n", - "\n", - "```python\n", - "import torch\n", - "from safetensors.torch import save_file\n", - "\n", - "# 加载模型\n", - "lora_model = torch.load(lora_model_bin_path, map_location=\"cpu\")\n", - "\n", - "# 转换为safetensor格式\n", - "save_file(lora_model, \"lora.safetensors\")\n", - "\n", - "```" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## LoRA && DreamBooth微调训练\n", - "\n", - "### DreamBooth简介\n", - "\n", - "DreamBooth是Google的研究人员于2022年提出的技术,支持在少量的图片上进行训练,然后将自定义的主题注入到扩散模型中。\n", - "\n", - "![](./resource/dreambooth.jpeg)\n", - "\n", - "图片来源: https://dreambooth.github.io/\n", - "\n", - "直接使用少量的图片文本数据集对扩散模型进行训练容易导致过拟合,或是语言漂移。DreamBooth使用以下方式避免了模型的退化:\n", - "\n", - "- 用户需要为新的主题选择一个罕见的词(标识符),模型将在训练过程中将这个词和图片的主题进行关联。\n", - "\n", - "- 为了避免过拟合和语言漂移,微调过程中,使用相同类别的图片参与训练(这些图片可以由扩散模型生成)。\n", - "\n", - "对于DreamBooth的详细介绍,用户可以参考[DreamBooth的博客](https://dreambooth.github.io/),以及[HuggingFace博客](https://huggingface.co/blog/dreambooth)对于DreamBooth的介绍。\n", - "\n", - "当通过DreamBooth训练扩散模型时,用户可以选择进行普通的微调(直接微调原始的模型参数),也可以使用LoRA的方式进行微调,在以下示例中,我们将使用LoRA的方式,进行DreamBooth训练。\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 准备训练数据集\n", - "\n", - "为了训练DreamBooth,用户需要准备特定风格的图片数据集,当前示例中,我们准备了数据集在`sks-dog`目录下。\n", - "\n", - "通过以下代码,我们将将数据集上传到OSS上,供训练作业使用。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pai.common.oss_utils import upload\n", - "\n", - "train_data_uri = upload(\"sks-dog\", \"stable_diffusion/dreambooth/train-sks-dog/\")" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 准备训练代码\n", - "\n", - "我们使用HuggingFace Diffusers库提供的训练脚本,通过LoRA && DreamBooth方式训练扩散模型。通过以下代码,我们下载训练脚本到本地。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# 创建训练脚本保存路径\n", - "!mkdir -p train_dreambooth/\n", - "\n", - "# 下载HuggingFace diffusers(v1.17.1)库提供的示例代码(因为访问GitHub的网络并不稳定,用户当出现下载失败,可以多尝试几次)\n", - "!wget https://raw.githubusercontent.com/huggingface/diffusers/v0.17.1/examples/dreambooth/train_dreambooth_lora.py -P train_dreambooth/" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "训练作业将使用PAI提供的PyTorch镜像运行脚本,我们需要通过以下的`requirements.txt`安装训练脚本依赖的库。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%%writefile train_dreambooth/requirements.txt\n", - "# %%writefile 指令会将当前内容写入到 train_dreambooth/requirements.txt 文件中\n", - "\n", - "diffusers>=0.17.1\n", - "\n", - "# source: https://github.com/huggingface/diffusers/blob/main/examples/dreambooth/requirements.txt\n", - "accelerate>=0.16.0,<=0.18.0 # diffusers 提供的示例代码(v0.17.1)无法运行在accelerate>=0.18.0上.\n", - "torchvision\n", - "transformers>=4.25.1,<5.0.0\n", - "ftfy\n", - "tensorboard\n", - "Jinja2" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 提交训练作业\n", - "\n", - "通过以下代码,我们使用PAI Python SDK,提交训练作业到PAI。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pai.estimator import Estimator\n", - "from pai.image import retrieve\n", - "\n", - "image_uri = retrieve(\n", - " \"PyTorch\",\n", - " \"latest\",\n", - " accelerator_type=\"GPU\",\n", - ").image_uri\n", - "\n", - "\n", - "# 训练作业启动命令,通过环境变量的方式获取:\n", - "# a)输入输出的模型/数据路径\n", - "# b)训练任务的超参数\n", - "command = \"\"\"accelerate launch train_dreambooth_lora.py \\\n", - " --pretrained_model_name_or_path=$PAI_INPUT_PRETRAINED_MODEL \\\n", - " --instance_data_dir=$PAI_INPUT_TRAIN_DATA \\\n", - " --output_dir=$PAI_OUTPUT_MODEL \\\n", - " --logging_dir=$PAI_OUTPUT_TENSORBOARD \\\n", - " --instance_prompt=\"$PAI_HPS_INSTANCE_PROMPT\" \\\n", - " --resolution=512 \\\n", - " --train_batch_size=$PAI_HPS_TRAIN_BATCH_SIZE \\\n", - " --gradient_accumulation_steps=$PAI_HPS_GRADIENT_ACCUMULATION_STEPS \\\n", - " --checkpointing_steps=$PAI_HPS_CHECKPOINTING_STEPS \\\n", - " --learning_rate=$PAI_HPS_LEARNING_RATE \\\n", - " --lr_scheduler=\"constant\" \\\n", - " --lr_warmup_steps=0 \\\n", - " --max_train_steps=$PAI_HPS_MAX_TRAIN_STEPS \\\n", - " --validation_prompt=\"$PAI_HPS_VALIDATION_PROMPT\" \\\n", - " --validation_epochs=$PAI_HPS_VALIDATION_EPOCHS \\\n", - " --seed=\"0\"\n", - " \"\"\"\n", - "\n", - "# 训练作业超参\n", - "hps = {\n", - " \"instance_prompt\": \"a photo of sks dog\", # 训练的图片数据文本使用的标注Prompt。这里的sks是我们使用的数据集的特定风格标识符。\n", - " \"validation_prompt\": \"a photo of sks dog in a bucket\", # 验证模型的Prompt\n", - " # \"class_prompt\": \"a photo of dog\", # 用于生成类别图片数据,避免模型过拟合&&语言偏移\n", - " \"validation_epochs\": 50, # 每隔50个epoch验证一次\n", - " \"max_train_steps\": 500, # 最大训练步数\n", - " \"learning_rate\": 1e-4, # 学习率\n", - " \"train_batch_size\": 1, # 训练batch size\n", - " \"gradient_accumulation_steps\": 1, # 梯度累积步数\n", - " \"checkpointing_steps\": 100, # 每隔100个step保存一次模型\n", - "}\n", - "\n", - "\n", - "est = Estimator(\n", - " image_uri=image_uri, # 训练作业使用的镜像\n", - " source_dir=\"train_dreambooth\", # 训练代码路径,代码会被上传,并准备到训练作业环境中\n", - " command=command, # 训练任务启动命令\n", - " instance_type=\"ecs.gn6i-c4g1.xlarge\", # 4 vCPU, 16 GiB 内存, 1 x NVIDIA T4 GPU\n", - " base_job_name=\"sd_lora_dreambooth_\", # 训练作业名称前缀\n", - " hyperparameters=hps, # 作业超参,训练命令和脚本可以通过 `PAI_HPS_{HP_NAME_UPPER_CASE}` 环境变量,或是读取`/ml/input/config/hpyerparameters.json`文件获取\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(\"Input PreTrainedModel: \", model.uri)\n", - "print(\"Input TrainData: \", train_data_uri)\n", - "\n", - "est.fit(\n", - " inputs={\n", - " \"pretrained_model\": model.uri,\n", - " \"train_data\": train_data_uri,\n", - " },\n", - " wait=True,\n", - ")" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "训练任务会在输出目录下,生成一个`pytorch_lora_weights.bin`的模型文件,相应的文件会被上传准备到用户的OSS中,用户可以通过以下的代码,将模型文件下载到本地。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "import posixpath\n", - "\n", - "from pai.common.oss_utils import download\n", - "\n", - "# 输出模型路径\n", - "output_lora_model = posixpath.join(est.model_data(), \"pytorch_lora_weights.bin\")\n", - "print(\"OutputModel: \", output_lora_model)\n", - "\n", - "model_path = download(output_lora_model, \"./lora_dreambooth_model/\")" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "获得的LoRA模型,用户可以通过Diffuser提供的API,在推理pipeline加载使用,用户可以参考diffuser的文档:[DreamBooth Inference](https://huggingface.co/docs/diffusers/training/lora#dreambooth-inference)。" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 结语\n", - "\n", - "通过当前示例,我们介绍了如何基于HuggingFace diffusers库,在PAI上完成StableDiffusion模型的LoRA微调训练。用户可以通过Diffuers库的API,直接在推理Pipeline中加载使用这些LoRA模型,也可以将模型转换Safetensors格式,用于StableDiffusionWebUI中。\n", - "\n", - "除了对于LoRA的支持,Diffusers库支持对于直接对扩散模型微调,也支持包括TextInversion, ControlNet, InstructPix2Pix等方式训练扩散模型,并且提供了相应的训练脚本和教程。用户同样可以参考本示例,在PAI运行这些训练任务。\n", - "\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 参考\n", - "\n", - "- HuggingFace LoRa Tutorial: https://huggingface.co/docs/diffusers/training/lora#texttoimage\n", - "\n", - "- HuggingFace LoRA Blog: https://huggingface.co/blog/lora\n", - "\n", - "- Google DreamBooth Blog:https://dreambooth.github.io/" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "base", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.3" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/source/tutorial/stable_diffusion_lora/train-data/cat1.jpg b/docs/source/tutorial/stable_diffusion_lora/train-data/cat1.jpg deleted file mode 100644 index 89d760d5ffcac726e8171da34ca31e13fc67fd4b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27340 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 Y%M@Cu^+UV*0S5Y0kWrPcl{5?fe?Ir@umAu6 literal 0 HcmV?d00001 diff --git a/assets/logo.svg b/assets/logo.svg new file mode 100644 index 0000000..422f09a --- /dev/null +++ b/assets/logo.svg @@ -0,0 +1 @@ + From b1d0a4224e0daa8dd0b3a15bf0d260853780b987 Mon Sep 17 00:00:00 2001 From: LiangQuan Date: Wed, 27 Dec 2023 15:08:01 +0800 Subject: [PATCH 04/59] Add github workflow (#5) * [to #53688079] Update README.md * Add Github workflow * Update Integration workflow * Update github workflow --------- Signed-off-by: pitt-liang --- .github/workflow/integration.yaml | 30 ++++++++++++++++++++++++++ .github/workflow/lint.yaml | 35 +++++++++++++++++++++++++++++++ .github/workflow/publish.yaml | 29 +++++++++++++++++++++++++ .github/workflow/unit.yaml | 22 +++++++++++++++++++ .pre-commit-config.yaml | 5 +++++ README.md | 14 ++++++------- README_CN.md | 14 ++++++------- 7 files changed, 135 insertions(+), 14 deletions(-) create mode 100644 .github/workflow/integration.yaml create mode 100644 .github/workflow/lint.yaml create mode 100644 .github/workflow/publish.yaml create mode 100644 .github/workflow/unit.yaml diff --git a/.github/workflow/integration.yaml b/.github/workflow/integration.yaml new file mode 100644 index 0000000..6563997 --- /dev/null +++ b/.github/workflow/integration.yaml @@ -0,0 +1,30 @@ +name: Integration test + +on: + pull_request_review: + types: [submitted, edited] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + integration-test: + # Integration test only works while the Pull Request is approved or the source repository is trusted repository. + if: github.event.review.state == 'APPROVED' || github.event.pull_request.head.repo.full_name == 'aliyun/pai-python-sdk' + runs-on: ubuntu-latest + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TEST_CONFIG_FILE: ${{ secrets.TEST_CONFIG_FILE }} + steps: + - uses: actions/checkout@v4 + - name: Set up Python 3.8 + uses: actions/setup-python@v5 + with: + python-version: "3.8" + - name: Prepare test configuration + run: echo $TEST_CONFIG_FILE > tests/integration/test.ini + - name: Install Nox + run: pip install nox + - name: Run integration test + run: nox -s integration diff --git a/.github/workflow/lint.yaml b/.github/workflow/lint.yaml new file mode 100644 index 0000000..05c5820 --- /dev/null +++ b/.github/workflow/lint.yaml @@ -0,0 +1,35 @@ +name: Lint test + +on: [push, pull_request] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + common-lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up Python 3.8 + uses: actions/setup-python@v5 + with: + python-version: "3.8" + - name: Install pre-commit hook + run: | + pip install pre-commit + - name: Linting + run: pre-commit run --all-files + doc-lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up Python 3.8 + uses: actions/setup-python@v5 + with: + python-version: "3.8" + - name: Install Nox + run: | + pip install nox + - name: Linting + run: nox -s doc diff --git a/.github/workflow/publish.yaml b/.github/workflow/publish.yaml new file mode 100644 index 0000000..0a0cd40 --- /dev/null +++ b/.github/workflow/publish.yaml @@ -0,0 +1,29 @@ +name: Publish Package +on: + push: + tags: + - 'v*' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + publish: + name: Publish Package + runs-on: ubuntu-latest + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} + steps: + - uses: actions/checkout@v4 + - name: Set up Python 3.8 + uses: actions/setup-python@v5 + with: + python-version: '3.8' + - name: Install dependencies + run: pip install wheel setuptools twine + - name: Build package + run: python setup.py sdist bdist_wheel + - name: Publish package to PyPI + run: twine upload dist/* --skip-existing -u __token__ -p $PYPI_TOKEN diff --git a/.github/workflow/unit.yaml b/.github/workflow/unit.yaml new file mode 100644 index 0000000..aa087d4 --- /dev/null +++ b/.github/workflow/unit.yaml @@ -0,0 +1,22 @@ +name: Unit test + +on: [push, pull_request] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up Python 3.8 + uses: actions/setup-python@v5 + with: + python-version: "3.8" + - name: Install Nox + run: | + pip install nox + - name: Linting + run: nox -s unit diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 89fd287..d7a06a5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -53,3 +53,8 @@ repos: rev: 0.6.1 hooks: - id: nbstripout + + - repo: https://github.com/gitleaks/gitleaks + rev: v8.16.1 + hooks: + - id: gitleaks diff --git a/README.md b/README.md index aaf9385..31275c7 100644 --- a/README.md +++ b/README.md @@ -13,11 +13,11 @@ Install the PAI Python SDK using the following command, which supports Python ve python -m pip install alipai ``` -## Documentation 📖 +## 📖 Documentation -Find detailed documentation, including API references and user guides, in the [docs]{.title-ref} directory or visit [PAI Python SDK Documentation](https://alipai.readthedocs.io/). +Find detailed documentation, including API references and user guides, in the [docs](./docs/) directory or visit [PAI Python SDK Documentation](https://alipai.readthedocs.io/). -## Basic Usage 🛠 +## 🛠 Basic Usage - Submit a custom training job @@ -69,16 +69,16 @@ p.predict( For more details, please refer to the [PAI Python SDK Documentation](https://alipai.readthedocs.io/). -## Contributing 🤝 +## 🤝 Contributing Contributions to the PAI Python SDK are welcome. Please read our contribution guidelines in the [CONTRIBUTING](./CONTRIBUTING.md) file. -## License 📝 +## 📝 License PAI Python SDK is developed by Alibaba Cloud and licensed under the Apache License (Version 2.0). -## Contact 📬 +## 📬 Contact For support or inquiries, please open an issue on the GitHub repository or contact us in the DingTalk group: -DingTalkGroup +DingTalkGroup diff --git a/README_CN.md b/README_CN.md index 942bbcb..6afe207 100644 --- a/README_CN.md +++ b/README_CN.md @@ -4,7 +4,7 @@ PAI Python SDK是阿里云 [机器学习平台 PAI(Platform for Artificial Intelligence)](https://www.aliyun.com/product/bigdata/learn) 提供的Python SDK,提供了更易用的HighLevel API,支持机器学习工程师简单地使用Python在PAI完成模型训练和部署,串联机器学习的流程。 -## 安装 🔧 +## 🔧 安装 使用以下命令安装PAI Python SDK(支持Python版本 \>= 3.6,建议使用Python版本 \>= 3.8): @@ -12,11 +12,11 @@ PAI Python SDK是阿里云 [机器学习平台 PAI(Platform for Artificial Intel python -m pip install alipai ``` -## 文档 📖 +## 📖 文档 请通过访问 [PAI Python SDK文档](https://alipai.readthedocs.io/) 或是查看 [docs](./docs) 目录下的文件获取SDK的详细文档,包括用户指南和API文档。 -## 使用示例 🛠 +## 🛠 使用示例 - 提交自定义训练任务 @@ -65,16 +65,16 @@ p.predict( 更多功能介绍,请参阅 [PAI Python SDK文档](https://alipai.readthedocs.io/) 。 -## 贡献代码 🤝 +## 🤝 贡献代码 我们欢迎为PAI Python SDK贡献代码。请阅读 [CONTRIBUTING](./CONTRIBUTING.md) 文件了解如何为本项目贡献代码。 -## 许可证 📝 +## 📝 许可证 PAI Python SDK是由阿里云开发,并根据Apache许可证(版本2.0)授权使用。 -## 联系方式 📬 +## 📬 联系方式 如需支持或咨询,请在GitHub仓库中提交issue,或通过钉钉群联系我们: -DingTalkGroup +DingTalkGroup From 10c9d54f65aec90820585f3dc86b39a507817cb6 Mon Sep 17 00:00:00 2001 From: LiangQuan Date: Wed, 27 Dec 2023 15:26:43 +0800 Subject: [PATCH 05/59] Fix Github workflow (#6) * Fix github workflow --------- Signed-off-by: pitt-liang --- .github/{workflow => workflows}/integration.yaml | 0 .github/{workflow => workflows}/lint.yaml | 0 .github/{workflow => workflows}/publish.yaml | 0 .github/{workflow => workflows}/unit.yaml | 0 .pre-commit-config.yaml | 2 +- 5 files changed, 1 insertion(+), 1 deletion(-) rename .github/{workflow => workflows}/integration.yaml (100%) rename .github/{workflow => workflows}/lint.yaml (100%) rename .github/{workflow => workflows}/publish.yaml (100%) rename .github/{workflow => workflows}/unit.yaml (100%) diff --git a/.github/workflow/integration.yaml b/.github/workflows/integration.yaml similarity index 100% rename from .github/workflow/integration.yaml rename to .github/workflows/integration.yaml diff --git a/.github/workflow/lint.yaml b/.github/workflows/lint.yaml similarity index 100% rename from .github/workflow/lint.yaml rename to .github/workflows/lint.yaml diff --git a/.github/workflow/publish.yaml b/.github/workflows/publish.yaml similarity index 100% rename from .github/workflow/publish.yaml rename to .github/workflows/publish.yaml diff --git a/.github/workflow/unit.yaml b/.github/workflows/unit.yaml similarity index 100% rename from .github/workflow/unit.yaml rename to .github/workflows/unit.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d7a06a5..fe4eb0d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -37,7 +37,7 @@ repos: - -w - repo: https://github.com/pycqa/isort - rev: 5.10.1 + rev: 5.12.0 hooks: - id: isort name: isort (python) From 0dd26a4c62f293dbfc5a55a8983f7ebbfc84674e Mon Sep 17 00:00:00 2001 From: LiangQuan Date: Tue, 2 Jan 2024 17:18:38 +0800 Subject: [PATCH 06/59] fix: Hacky way to wait prediction service to be ready (#8) * fix: Hacky way to wait prediction service to be ready * ci: Fix github workflow * ci: Fix integration test. --------- Signed-off-by: pitt-liang --- .github/workflows/integration.yaml | 7 ++-- .github/workflows/lint.yaml | 2 +- .github/workflows/unit.yaml | 2 +- noxfile.py | 8 ++++- pai/common/utils.py | 6 ++++ pai/estimator.py | 33 ++++++++++++------ pai/huggingface/model.py | 2 +- pai/model.py | 15 ++++++--- pai/predictor.py | 52 ++++++++++++++++++++++++++--- tests/integration/test_predictor.py | 5 --- tests/integration/utils.py | 2 +- 11 files changed, 100 insertions(+), 34 deletions(-) diff --git a/.github/workflows/integration.yaml b/.github/workflows/integration.yaml index 6563997..e70a926 100644 --- a/.github/workflows/integration.yaml +++ b/.github/workflows/integration.yaml @@ -1,8 +1,9 @@ name: Integration test on: - pull_request_review: - types: [submitted, edited] + push: + branches: + - master concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -10,8 +11,6 @@ concurrency: jobs: integration-test: - # Integration test only works while the Pull Request is approved or the source repository is trusted repository. - if: github.event.review.state == 'APPROVED' || github.event.pull_request.head.repo.full_name == 'aliyun/pai-python-sdk' runs-on: ubuntu-latest env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 05c5820..1acf4fe 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -1,6 +1,6 @@ name: Lint test -on: [push, pull_request] +on: [push] concurrency: group: ${{ github.workflow }}-${{ github.ref }} diff --git a/.github/workflows/unit.yaml b/.github/workflows/unit.yaml index aa087d4..de74d41 100644 --- a/.github/workflows/unit.yaml +++ b/.github/workflows/unit.yaml @@ -1,6 +1,6 @@ name: Unit test -on: [push, pull_request] +on: [push] concurrency: group: ${{ github.workflow }}-${{ github.ref }} diff --git a/noxfile.py b/noxfile.py index 244846e..68d4a0d 100644 --- a/noxfile.py +++ b/noxfile.py @@ -50,6 +50,12 @@ def integration(session: Session): if os.environ.get(key, value) is not None } + # set worker to 2 * cpu_count (physical cores) if not specified + if "-n" not in session.posargs and "--numprocesses" not in session.posargs: + pos_args = session.posargs + ["-n", str(os.cpu_count() * 2)] + else: + pos_args = session.posargs + session.run( "pytest", "--cov-config=.coveragerc", @@ -57,7 +63,7 @@ def integration(session: Session): "--cov-report=html", "--cov=pai", os.path.join("tests", "integration"), - *session.posargs, + *pos_args, env=env, ) session.run( diff --git a/pai/common/utils.py b/pai/common/utils.py index 2e3c654..45bb80d 100644 --- a/pai/common/utils.py +++ b/pai/common/utils.py @@ -88,12 +88,18 @@ def make_list_resource_iterator(method: Callable, **kwargs): kwargs.update(page_number=page_number, page_size=page_size) result = method(**kwargs) if isinstance(result, PaginatedResult): + total_count = result.total_count result = result.items + else: + total_count = None for item in result: yield item if len(result) == 0 or len(result) < page_size: return + if total_count and page_number * page_size >= total_count: + return + page_number += 1 diff --git a/pai/estimator.py b/pai/estimator.py index 5441692..da85be6 100644 --- a/pai/estimator.py +++ b/pai/estimator.py @@ -29,6 +29,9 @@ from datetime import datetime from typing import Any, Dict, List, Optional, Union +from Tea.exceptions import TeaException + +from .api.base import PaginatedResult from .api.entity_base import EntityBaseMixin from .common import ProviderAlibabaPAI, git_utils from .common.consts import INSTANCE_TYPE_LOCAL_GPU, FileSystemInputScheme, JobType @@ -1521,7 +1524,9 @@ def _normalize_name(name: str) -> str: for name, value in self.estimator.hyperparameters.items(): env[_TrainingEnv.ENV_PAI_HPS_PREFIX + _normalize_name(name)] = str(value) user_args.extend(["--" + name, shlex.quote(str(value))]) - env[_TrainingEnv.ENV_PAI_USER_ARGS] = shlex.join(user_args) + env[_TrainingEnv.ENV_PAI_USER_ARGS] = " ".join( + [shlex.quote(v) for v in user_args] + ) env[_TrainingEnv.ENV_PAI_HPS] = json.dumps( {name: str(value) for name, value in self.estimator.hyperparameters.items()} ) @@ -1878,15 +1883,27 @@ def __init__( self._future = None self._stop = False - def _list_logs(self): - page_number, page_offset = 1, 0 - # print training job logs. - while not self._stop: + def _list_logs_api(self, page_number: int = 1): + try: res = self.session.training_job_api.list_logs( self.training_job_id, page_number=page_number, page_size=self.page_size, ) + return res + except TeaException as e: + # hack: Backend service may raise an exception when the training job + # instance is not found. + if e.code == "TRAINING_JOB_INSTANCE_NOT_FOUND": + return PaginatedResult(items=[], total_count=0) + else: + raise e + + def _list_logs(self): + page_number, page_offset = 1, 0 + # print training job logs. + while not self._stop: + res = self._list_logs_api(page_number=page_number) # 1. move to next page if len(res.items) == self.page_size: # print new logs starting from page_offset @@ -1904,11 +1921,7 @@ def _list_logs(self): # When _stop is True, wait and print remaining logs. time.sleep(10) while True: - res = self.session.training_job_api.list_logs( - self.training_job_id, - page_number=page_number, - page_size=self.page_size, - ) + res = self._list_logs_api(page_number=page_number) # There maybe more logs in the next page if len(res.items) == self.page_size: self._print_logs(logs=res.items[page_offset:]) diff --git a/pai/huggingface/model.py b/pai/huggingface/model.py index 7b46b7a..1771470 100644 --- a/pai/huggingface/model.py +++ b/pai/huggingface/model.py @@ -259,7 +259,7 @@ def _get_supported_tf_versions_for_inference(self) -> List[str]: return res def _get_latest_tf_version_for_inference(self) -> str: - """Return the latest Transformers version for inference.""" + """Return the latest transformers version for inference.""" res = self._get_supported_tf_versions_for_inference() return max( res, diff --git a/pai/model.py b/pai/model.py index b631231..609c354 100644 --- a/pai/model.py +++ b/pai/model.py @@ -816,7 +816,6 @@ def _deploy( ) if wait: predictor.wait_for_ready() - time.sleep(5) return predictor @@ -987,12 +986,18 @@ def _deploy_local( # build command to install requirements if requirements_list: - install_requirements = shlex.join( - ["python", "-m", "pip", "install"] + requirements_list + install_requirements = " ".join( + [ + shlex.quote(s) + for s in ["python", "-m", "pip", "install"] + requirements_list + ] ) elif requirements_path: - install_requirements = shlex.join( - ["python", "-m", "pip", "install", "-r", requirements_path] + install_requirements = " ".join( + [ + shlex.quote(s) + for s in ["python", "-m", "pip", "install", "-r", requirements_path] + ] ) else: install_requirements = "" diff --git a/pai/predictor.py b/pai/predictor.py index fdd06e5..fc2cb88 100644 --- a/pai/predictor.py +++ b/pai/predictor.py @@ -73,7 +73,6 @@ def completed_status(cls): class EndpointType(object): - # Public Internet Endpoint INTERNET = "INTERNET" @@ -82,7 +81,6 @@ class EndpointType(object): class ServiceType(object): - Standard = "Standard" Async = "Async" @@ -296,22 +294,66 @@ def delete_service(self): """Delete the service.""" self.session.service_api.delete(name=self.service_name) - def wait_for_ready(self): - """Wait until the service enter running status.""" + def wait_for_ready(self, force: bool = False): + """Wait until the service enter running status. + + Args: + force (bool): Whether to force wait for ready. + + Raises: + RuntimeError: Raise if the service terminated unexpectedly. + + """ + if self.service_status == ServiceStatus.Running and not force: + return + logger.info( "Service waiting for ready: service_name={}".format(self.service_name) ) unexpected_status = ServiceStatus.completed_status() unexpected_status.remove(ServiceStatus.Running) - type(self)._wait_for_status( service_name=self.service_name, status=ServiceStatus.Running, unexpected_status=unexpected_status, session=self.session, ) + + # hack: PAI-EAS gateway may not be ready when the service is ready. + self._wait_for_gateway_ready() self.refresh() + def _wait_for_gateway_ready(self, attempts: int = 30, interval: int = 2): + """Hacky way to wait for the service gateway to be ready. + + Args: + attempts (int): Number of attempts to wait for the service gateway to be + ready. + interval (int): Interval between each attempt. + """ + + def _is_gateway_not_ready(resp: requests.Response): + return resp.status_code == 503 and resp.content == b"no healthy upstream" + + err_count_threshold = 3 + err_count = 0 + while attempts > 0: + attempts -= 1 + try: + # Send a probe request to the service. + resp = self._send_request(method="GET") + if not _is_gateway_not_ready(resp): + logger.info("Gateway for the service is ready.") + break + except requests.exceptions.RequestException as e: + err_count += 1 + if err_count >= err_count_threshold: + logger.warning("Failed to check gateway status: %s", e) + break + time.sleep(interval) + else: + logger.warning("Timeout waiting for gateway to be ready.") + @classmethod def _wait_for_status( cls, diff --git a/tests/integration/test_predictor.py b/tests/integration/test_predictor.py index 10278fc..f7b6b5a 100644 --- a/tests/integration/test_predictor.py +++ b/tests/integration/test_predictor.py @@ -14,7 +14,6 @@ import json import os -import time import numpy as np @@ -85,8 +84,6 @@ def _init_predictor(cls): p = Predictor(service_name=service_name) p.wait_for_ready() - # hack: wait for service to be really ready - time.sleep(15) return p @classmethod @@ -235,8 +232,6 @@ def _init_predictor(cls): p = Predictor(service_name=service_name) p.wait_for_ready() - # hack: wait for service to be really ready - time.sleep(15) return p @classmethod diff --git a/tests/integration/utils.py b/tests/integration/utils.py index bc19f51..dbca156 100644 --- a/tests/integration/utils.py +++ b/tests/integration/utils.py @@ -97,7 +97,7 @@ def is_inner(self): @classmethod def _load_test_config(cls): - test_config = os.environ.get("PAI_TEST_CONFIG", "test_public.ini") + test_config = os.environ.get("PAI_TEST_CONFIG", "test.ini") cfg_parser = configparser.ConfigParser() cfg_parser.read(os.path.join(_test_root, test_config)) From f972256e121d805a0fc3ad8c650e041e5b55de4e Mon Sep 17 00:00:00 2001 From: Yu Zhao Date: Tue, 2 Jan 2024 17:38:08 +0800 Subject: [PATCH 07/59] fix: update ModelScopeEstimator image (#7) * fix: update ModelScopeEstimator image * fix: fix duplicate filter * fix: update huggingface-inference and modelscope-inference image --------- Co-authored-by: yutou.zy --- pai/huggingface/estimator.py | 3 +++ pai/huggingface/model.py | 34 +++++++++++++++++++--------------- pai/modelscope/estimator.py | 11 +++++------ pai/modelscope/model.py | 30 ++++++++++++++++++------------ 4 files changed, 45 insertions(+), 33 deletions(-) diff --git a/pai/huggingface/estimator.py b/pai/huggingface/estimator.py index e8e3d7b..99ffbc2 100644 --- a/pai/huggingface/estimator.py +++ b/pai/huggingface/estimator.py @@ -196,6 +196,8 @@ def __init__( session=session, **kwargs, ) + # Check image_uri and transformers_version + self.training_image_uri() def _validate_image_uri(self, image_uri: str, transformers_version: str) -> None: """Check if image_uri or transformers_version arguments are specified.""" @@ -272,6 +274,7 @@ def _get_supported_tf_versions_for_training(self) -> List[str]: if label["Value"] not in res: res.append(label["Value"]) + res.sort(key=lambda x: to_semantic_version(x)) return res def _get_latest_tf_version_for_training(self) -> str: diff --git a/pai/huggingface/model.py b/pai/huggingface/model.py index 1771470..ca85178 100644 --- a/pai/huggingface/model.py +++ b/pai/huggingface/model.py @@ -13,7 +13,6 @@ # limitations under the License. import logging -import re from typing import Any, Dict, List, Optional, Union from ..common.utils import to_semantic_version @@ -29,10 +28,6 @@ logger = logging.getLogger(__name__) -_PAI_HF_IMAGE_TAG_PATTERN_INFERENCE = re.compile( - r"huggingface-inference:transformers-(\d.+)-(gpu|cpu)" -) - class HuggingFaceModel(ModelBase): """A HuggingFace ``Model`` that can be deployed in PAI to create a prediction service. @@ -180,6 +175,8 @@ def __init__( model_data=self.model_data, session=session or get_default_session(), ) + # Check image_uri and transformers_version + self.serving_image_uri() def _validate_args(self, image_uri: str, transformers_version: str) -> None: """Check if image_uri or transformers_version arguments are specified.""" @@ -189,7 +186,7 @@ def _validate_args(self, image_uri: str, transformers_version: str) -> None: "Specify either transformers_version or image_uri." ) - def serving_image_uri(self, instance_type: str) -> str: + def serving_image_uri(self) -> str: """Return the Docker image to use for serving. The :meth:`pai.huggingface.model.HuggingFaceModel.deploy` method, that does the @@ -212,10 +209,13 @@ def serving_image_uri(self, instance_type: str) -> str: # Filter images by Transformers version if self.transformers_version == "latest": latest_version = self._get_latest_tf_version_for_inference() - name = f"huggingface-inference:transformers-{latest_version}-" + labels.append(ImageLabel.framework_version("Transformers", latest_version)) else: - name = f"huggingface-inference:transformers-{self.transformers_version}-" + labels.append( + ImageLabel.framework_version("Transformers", self.transformers_version) + ) + name = "huggingface-inference:" resp = self.session.image_api.list( name=name, labels=labels, @@ -241,21 +241,25 @@ def _get_supported_tf_versions_for_inference(self) -> List[str]: ImageLabel.EAS_LABEL, ImageLabel.PROVIDER_PAI_LABEL, ImageLabel.DEVICE_TYPE_GPU, + ImageLabel.framework_version("Transformers", "*"), ] - name = "huggingface-inference:transformers-" + name = "huggingface-inference:" list_images = self.session.image_api.list( name=name, labels=labels, + verbose=True, workspace_id=0, ).items res = [] for image in list_images: - tag_match = _PAI_HF_IMAGE_TAG_PATTERN_INFERENCE.match(image["Name"]) - transformer_version, _ = tag_match.groups() - if transformer_version not in res: - res.append(transformer_version) - + for label in image["Labels"]: + if ( + label["Key"] == "system.framework.Transformers" + and label["Value"] not in res + ): + res.append(label["Value"]) + res.sort(key=lambda x: to_semantic_version(x)) return res def _get_latest_tf_version_for_inference(self) -> str: @@ -327,7 +331,7 @@ def deploy( :class:`pai.predictor.Predictor` : A PAI ``Predictor`` instance used for making prediction to the prediction service. """ - image_uri = self.serving_image_uri(instance_type=instance_type) + image_uri = self.serving_image_uri() self.inference_spec = container_serving_spec( command=self.command, image_uri=image_uri, diff --git a/pai/modelscope/estimator.py b/pai/modelscope/estimator.py index 94ba318..43ce15d 100644 --- a/pai/modelscope/estimator.py +++ b/pai/modelscope/estimator.py @@ -196,6 +196,8 @@ def __init__( session=session, **kwargs, ) + # Check image_uri and modelscope_version + self.training_image_uri() def _validate_image_uri(self, image_uri: str, modelscope_version: str) -> None: """Check if image_uri or modelscope_version arguments are specified.""" @@ -219,10 +221,8 @@ def training_image_uri(self) -> str: labels = [ ImageLabel.OFFICIAL_LABEL, - ImageLabel.DLC_LABEL, - ImageLabel.PROVIDER_COMMUNITY_LABEL, + ImageLabel.DSW_LABEL, ImageLabel.DEVICE_TYPE_GPU, - ImageLabel.framework_version("PyTorch", "*"), ] # Filter images by ModelScope version @@ -255,10 +255,8 @@ def _get_supported_ms_versions_for_training(self) -> List[str]: label_keys = "system.framework.ModelScope" label_filter = [ ImageLabel.OFFICIAL_LABEL, - ImageLabel.DLC_LABEL, - ImageLabel.PROVIDER_COMMUNITY_LABEL, + ImageLabel.DSW_LABEL, ImageLabel.DEVICE_TYPE_GPU, - ImageLabel.framework_version("PyTorch", "*"), ImageLabel.framework_version("ModelScope", "*"), ] list_image_labels = self.session.image_api.list_labels( @@ -272,6 +270,7 @@ def _get_supported_ms_versions_for_training(self) -> List[str]: if label["Value"] not in res: res.append(label["Value"]) + res.sort(key=lambda x: to_semantic_version(x)) return res def _get_latest_ms_version_for_training(self) -> str: diff --git a/pai/modelscope/model.py b/pai/modelscope/model.py index 662ea90..f74d0e7 100644 --- a/pai/modelscope/model.py +++ b/pai/modelscope/model.py @@ -13,7 +13,6 @@ # limitations under the License. import logging -import re from typing import Any, Dict, List, Optional, Union from ..api.image import ImageLabel @@ -29,8 +28,6 @@ logger = logging.getLogger(__name__) -_PAI_MS_IMAGE_TAG_PATTERN_INFERENCE = re.compile(r"modelscope-inference:(\d.+)") - class ModelScopeModel(ModelBase): """A ModelScope ``Model`` that can be deployed in PAI to create a prediction service. @@ -175,6 +172,8 @@ def __init__( model_data=self.model_data, session=session, ) + # Check image_uri and modelscope_version + self.serving_image_uri() def _validate_args(self, image_uri: str, modelscope_version: str) -> None: """Check if image_uri or modelscope_version arguments are specified.""" @@ -184,7 +183,7 @@ def _validate_args(self, image_uri: str, modelscope_version: str) -> None: "Specify either modelscope_version or image_uri." ) - def serving_image_uri(self, instance_type: str) -> str: + def serving_image_uri(self) -> str: """Return the Docker image to use for serving. The :meth:`pai.modelscope.model.ModelScopeModel.deploy` method, that does the @@ -208,10 +207,13 @@ def serving_image_uri(self, instance_type: str) -> str: # Filter images by Transformers version if self.modelscope_version == "latest": latest_version = self._get_latest_ms_version_for_inference() - name = f"modelscope-inference:{latest_version}" + labels.append(ImageLabel.framework_version("ModelScope", latest_version)) else: - name = f"modelscope-inference:{self.modelscope_version}" + labels.append( + ImageLabel.framework_version("ModelScope", self.modelscope_version) + ) + name = "modelscope-inference:" list_images = self.session.image_api.list( name=name, labels=labels, @@ -236,21 +238,25 @@ def _get_supported_ms_versions_for_inference(self) -> List[str]: ImageLabel.EAS_LABEL, ImageLabel.PROVIDER_PAI_LABEL, ImageLabel.DEVICE_TYPE_GPU, + ImageLabel.framework_version("ModelScope", "*"), ] name = "modelscope-inference:" list_images = self.session.image_api.list( name=name, labels=labels, + verbose=True, workspace_id=0, ).items res = [] for image in list_images: - tag_match = _PAI_MS_IMAGE_TAG_PATTERN_INFERENCE.match(image["Name"]) - (modelscope_version,) = tag_match.groups() - if modelscope_version not in res: - res.append(modelscope_version) - + for label in image["Labels"]: + if ( + label["Key"] == "system.framework.ModelScope" + and label["Value"] not in res + ): + res.append(label["Value"]) + res.sort(key=lambda x: to_semantic_version(x)) return res def _get_latest_ms_version_for_inference(self) -> str: @@ -322,7 +328,7 @@ def deploy( :class:`pai.predictor.Predictor` : A PAI ``Predictor`` instance used for making prediction to the prediction service. """ - image_uri = self.serving_image_uri(instance_type=instance_type) + image_uri = self.serving_image_uri() self.inference_spec = container_serving_spec( command=self.command, image_uri=image_uri, From 05e391fe6a143cdc740b0a8edfd4166bd311a200 Mon Sep 17 00:00:00 2001 From: LiangQuan Date: Wed, 3 Jan 2024 13:46:42 +0800 Subject: [PATCH 08/59] release v0.4.5 (#9) Signed-off-by: pitt-liang --- pai/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pai/version.py b/pai/version.py index 7e627c0..16a1245 100644 --- a/pai/version.py +++ b/pai/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -VERSION = "0.4.5.dev0" +VERSION = "0.4.5" From 9ff250e6fd016e7f1f5a29f71f44a448acce7c36 Mon Sep 17 00:00:00 2001 From: LiangQuan Date: Mon, 26 Feb 2024 17:16:44 +0800 Subject: [PATCH 09/59] Supports dedicated resource && fix list training job logs. (#11) * Fix list training_job logs. * Supports dedicated resource. --------- Signed-off-by: pitt-liang --- pai/api/training_job.py | 31 ++++++++++++++++++++---- pai/estimator.py | 40 +++++++++++++------------------ pai/model.py | 35 +++++++++++++++++---------- pai/version.py | 2 +- tests/unit/test_inference_spec.py | 7 +++--- 5 files changed, 69 insertions(+), 46 deletions(-) diff --git a/pai/api/training_job.py b/pai/api/training_job.py index 64dcc91..5c99c08 100644 --- a/pai/api/training_job.py +++ b/pai/api/training_job.py @@ -19,6 +19,7 @@ AlgorithmSpec, CreateTrainingJobRequest, CreateTrainingJobRequestComputeResource, + CreateTrainingJobRequestComputeResourceInstanceSpec, CreateTrainingJobRequestHyperParameters, CreateTrainingJobRequestInputChannels, CreateTrainingJobRequestLabels, @@ -85,6 +86,8 @@ def create( instance_type, instance_count, job_name, + instance_spec: Optional[Dict[str, str]] = None, + resource_id: Optional[str] = None, hyperparameters: Optional[Dict[str, Any]] = None, input_channels: Optional[List[Dict[str, Any]]] = None, output_channels: Optional[List[Dict[str, Any]]] = None, @@ -119,10 +122,22 @@ def create( CreateTrainingJobRequestOutputChannels().from_map(ch) for ch in output_channels ] - compute_resource = CreateTrainingJobRequestComputeResource( - ecs_count=instance_count, - ecs_spec=instance_type, - ) + if instance_type: + compute_resource = CreateTrainingJobRequestComputeResource( + ecs_count=instance_count, + ecs_spec=instance_type, + ) + elif instance_spec: + compute_resource = CreateTrainingJobRequestComputeResource( + resource_id=resource_id, + instance_count=instance_count, + instance_spec=CreateTrainingJobRequestComputeResourceInstanceSpec().from_map( + instance_spec + ), + ) + else: + raise ValueError("Please provide instance_type or instance_spec.") + hyper_parameters = [ CreateTrainingJobRequestHyperParameters( name=name, @@ -186,4 +201,10 @@ def list_logs( training_job_id=training_job_id, request=request, ) - return self.make_paginated_result(resp) + # resp.logs may be None + logs = resp.logs or [] + total_count = resp.total_count or 0 + return PaginatedResult( + items=logs, + total_count=total_count, + ) diff --git a/pai/estimator.py b/pai/estimator.py index da85be6..dc9cd44 100644 --- a/pai/estimator.py +++ b/pai/estimator.py @@ -239,6 +239,8 @@ def __init__( output_path: Optional[str] = None, checkpoints_path: Optional[str] = None, instance_type: Optional[str] = None, + instance_spec: Optional[Dict] = None, + resource_id: Optional[Dict] = None, instance_count: Optional[int] = None, user_vpc_config: Optional[UserVpcConfig] = None, session: Optional[Session] = None, @@ -311,6 +313,8 @@ def __init__( """ self.hyperparameters = hyperparameters or dict() self.instance_type = instance_type + self.instance_spec = instance_spec + self.resource_id = resource_id self.instance_count = instance_count if instance_count else 1 self.max_run_time = max_run_time self.base_job_name = base_job_name @@ -320,8 +324,6 @@ def __init__( self.session = session or get_default_session() self._latest_training_job = None - self._check_instance_type() - def set_hyperparameters(self, **kwargs): """Set hyperparameters for the training job. @@ -335,16 +337,6 @@ def latest_training_job(self): """Return the latest submitted training job.""" return self._latest_training_job - def _check_instance_type(self): - """Check if the given instance_type is supported for training job.""" - if not is_local_run_instance_type( - self.instance_type - ) and not self.session.is_supported_training_instance(self.instance_type): - raise ValueError( - f"Instance type='{self.instance_type}' is not supported." - " Please provide a supported instance type to create the job." - ) - def _gen_job_display_name(self, job_name=None): """Generate job display name.""" if job_name: @@ -663,7 +655,9 @@ def __init__( instance_type: Optional[str] = None, instance_count: Optional[int] = None, user_vpc_config: Optional[UserVpcConfig] = None, + resource_id: Optional[str] = None, session: Optional[Session] = None, + **kwargs, ): """Estimator constructor. @@ -991,7 +985,9 @@ def _fit(self, job_name, inputs: Dict[str, Any] = None): training_job_id = self.session.training_job_api.create( instance_count=self.instance_count, + instance_spec=self.instance_spec, instance_type=self.instance_type, + resource_id=self.resource_id, job_name=job_name, hyperparameters=self.hyperparameters, max_running_in_seconds=self.max_run_time, @@ -1075,6 +1071,8 @@ def __init__( instance_count: Optional[int] = None, user_vpc_config: Optional[UserVpcConfig] = None, session: Optional[Session] = None, + instance_spec: Optional[Dict[str, Union[int, str]]] = None, + **kwargs, ): """Initialize an AlgorithmEstimator. @@ -1150,17 +1148,19 @@ def __init__( self.algorithm_provider = None self.algorithm_spec = algorithm_spec + if not instance_type and not instance_spec: + instance_type = self._get_default_training_instance_type() super(AlgorithmEstimator, self).__init__( hyperparameters=self._get_hyperparameters(hyperparameters), base_job_name=base_job_name, max_run_time=max_run_time, output_path=output_path, - instance_type=instance_type - if instance_type - else self._get_default_training_instance_type(), + instance_type=instance_type, instance_count=instance_count, session=session, user_vpc_config=user_vpc_config, + instance_spec=instance_spec, + **kwargs, ) # TODO: check if the hyperparameters are valid @@ -1223,14 +1223,6 @@ def _check_args( " The provided algorithm_spec will be ignored." ) - def _check_instance_type(self): - """Check if the given instance_type is supported for training job.""" - if not self.session.is_supported_training_instance(self.instance_type): - raise ValueError( - f"Instance type='{self.instance_type}' is not supported." - " Please provide a supported instance type to create the job." - ) - def _get_algo_version( self, algorithm_name: str, @@ -1391,6 +1383,8 @@ def _fit(self, job_name, inputs: Dict[str, Any] = None): training_job_id = self.session.training_job_api.create( instance_count=self.instance_count, instance_type=self.instance_type, + instance_spec=self.instance_spec, + resource_id=self.resource_id, job_name=job_name, hyperparameters=self.hyperparameters, max_running_in_seconds=self.max_run_time, diff --git a/pai/model.py b/pai/model.py index 609c354..70022c3 100644 --- a/pai/model.py +++ b/pai/model.py @@ -325,19 +325,20 @@ def mount( ) if "storage" in self._cfg_dict: - configs = self._cfg_dict.get("storage", []) + storages = copy.deepcopy(self._cfg_dict.get("storage", [])) else: - configs = [] + storages = [] + configs = [] uris = set() - for conf in configs: - # check if target mount path is already used. - if conf.get("mount_path") == mount_path: - raise MountPathIsOccupiedException( - f"The mount path '{mount_path}' has already been used." - ) - mount_uri = conf.get("oss", {}).get("path") - uris.add(mount_uri) + for s in storages: + # overwrite the existing mount path + if s.get("mount_path") == mount_path: + continue + oss_uri = s.get("oss", {}).get("path") + if oss_uri: + uris.add(oss_uri) + configs.append(s) if is_oss_uri(source): oss_uri_obj = OssUriObj(source) @@ -1758,6 +1759,7 @@ def get_estimator( base_job_name: Optional[str] = None, output_path: Optional[str] = None, max_run_time: Optional[int] = None, + **kwargs, ): """Generate an AlgorithmEstimator. @@ -1828,10 +1830,15 @@ def get_estimator( max_run_time = ts.get("Scheduler", {}).get("MaxRunningTimeInSeconds") train_compute_resource = ts.get("ComputeResource") - if train_compute_resource and (not instance_type or not instance_count): - # If instance_type or instance_count is not provided, use the default + instance_spec = kwargs.get("instance_spec") + if train_compute_resource: instance_type = instance_type or train_compute_resource.get("EcsSpec") - instance_count = instance_count or train_compute_resource.get("EcsCount") + instance_count = ( + instance_count + or train_compute_resource.get("EcsCount") + or train_compute_resource.get("InstanceCount") + ) + instance_spec = instance_spec or train_compute_resource.get("InstanceSpec") return AlgorithmEstimator( algorithm_name=algorithm_name, @@ -1843,7 +1850,9 @@ def get_estimator( max_run_time=max_run_time, instance_type=instance_type, instance_count=instance_count, + instance_spec=instance_spec, output_path=output_path, + **kwargs, ) def get_estimator_inputs(self) -> Dict[str, str]: diff --git a/pai/version.py b/pai/version.py index 16a1245..00d3243 100644 --- a/pai/version.py +++ b/pai/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -VERSION = "0.4.5" +VERSION = "0.4.5.post1" diff --git a/tests/unit/test_inference_spec.py b/tests/unit/test_inference_spec.py index 49733ce..da31ef4 100644 --- a/tests/unit/test_inference_spec.py +++ b/tests/unit/test_inference_spec.py @@ -90,7 +90,6 @@ def test_inference_spec(self): "oss://pai-sdk-example/path/to/model/", mount_path="/ml/world/" ) - with self.assertRaises(MountPathIsOccupiedException): - infer_spec.mount( - "oss://pai-sdk-example/path/to/abc/", mount_path="/ml/code/" - ) + infer_spec.mount( + "oss://pai-sdk-example/path/to/abc/edfg", mount_path="/ml/code/" + ) From 3ea7b77689da9844afd915a31d24649a79cac120 Mon Sep 17 00:00:00 2001 From: yangziming Date: Wed, 24 Jan 2024 09:48:09 +0800 Subject: [PATCH 10/59] feat: add Processor for submitting ProcessingJob --- pai/common/consts.py | 24 ++ pai/common/utils.py | 13 + pai/processor.py | 504 ++++++++++++++++++++++++++++ tests/integration/__init__.py | 9 + tests/integration/test_processor.py | 59 ++++ 5 files changed, 609 insertions(+) create mode 100644 pai/processor.py create mode 100644 tests/integration/test_processor.py diff --git a/pai/common/consts.py b/pai/common/consts.py index a5a46d1..d37eac4 100644 --- a/pai/common/consts.py +++ b/pai/common/consts.py @@ -104,3 +104,27 @@ class FileSystemInputScheme(object): CPFS = "cpfs" # BMCPFS file system type BMCPFS = "bmcpfs" + + +class DefaultChannelName(object): + MODEL = "model" + CHECKPOINT = "checkpoints" + TENSORBOARD = "tensorboard" + + +class StoragePathCategory(object): + """PAI builtin remote storage path.""" + + # For inference + InferenceSrc = "inference_src" + + # For training job + TrainingSrc = "training_src" + TrainingJob = "training_job" + TrainData = "train_data" + ModelData = "model_data" + + # For processing job + ProcessingJob = "processing_job" + ProcessingSrc = "processing_src" + InputData = "input_data" diff --git a/pai/common/utils.py b/pai/common/utils.py index 45bb80d..ec543d3 100644 --- a/pai/common/utils.py +++ b/pai/common/utils.py @@ -219,6 +219,19 @@ def is_filesystem_uri(uri: str) -> bool: return any(uri.startswith(f"{schema}://") for schema in schemas) +def is_dataset_id(item: str) -> bool: + """Return True if given input is a dataset ID. + + Args: + item (str): user input dataset ID. + + Examples: + >>> is_dataset_id('d-ybko3rap60c4gs9flc') + True + """ + return item.startswith("d-") + + @lru_cache() def is_domain_connectable(domain: str, port: int = 80, timeout: int = 1) -> bool: """Check if the domain is connectable.""" diff --git a/pai/processor.py b/pai/processor.py new file mode 100644 index 0000000..7e17399 --- /dev/null +++ b/pai/processor.py @@ -0,0 +1,504 @@ +# Copyright 2024 Alibaba, Inc. or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import logging +import os +import posixpath +from datetime import datetime +from typing import Any, Dict, List, Optional, Union + +from .common.consts import DefaultChannelName, JobType, StoragePathCategory +from .common.oss_utils import OssUriObj, is_oss_uri, upload +from .common.utils import ( + is_dataset_id, + is_filesystem_uri, + is_odps_table_uri, + random_str, + to_plain_text, +) +from .estimator import FileSystemInputBase, UserVpcConfig +from .estimator import _TrainingJob as _Job +from .session import Session, get_default_session + +logger = logging.getLogger(__name__) + + +def build_code_input( + source_dir: str, upload_data_path: str +) -> Optional[Dict[str, Any]]: + """Upload local code and build CodeDir config for job.""" + if not source_dir: + return + + from pai.session import get_default_session + + sess = get_default_session() + + if is_oss_uri(source_dir): + code_oss_uri = source_dir + elif os.path.exists(source_dir): + code_oss_uri = upload( + source_path=source_dir, + oss_path=upload_data_path, + bucket=sess.oss_bucket, + is_tar=True, + ) + else: + raise ValueError(f"Source directory {source_dir} does not exist.") + + code_oss_obj = OssUriObj(uri=sess.patch_oss_endpoint(code_oss_uri)) + res = { + "LocationType": "oss", + "LocationValue": { + "Bucket": code_oss_obj.bucket_name, + "Key": code_oss_obj.object_key, + "Endpoint": code_oss_obj.endpoint, + }, + } + + return res + + +def get_input_channel_config( + item: Optional[Union[str, FileSystemInputBase]] +) -> Dict[str, str]: + """Get channel config from given job input.""" + + if not isinstance(item, (str, FileSystemInputBase)): + raise ValueError(f"Input data of type {type(item)} is not supported.") + + if isinstance(item, FileSystemInputBase): + config = {"InputUri": item.to_input_uri()} + elif is_oss_uri(item) or is_filesystem_uri(item) or is_odps_table_uri(item): + config = {"InputUri": item} + elif is_dataset_id(item): + config = {"DatasetId": item} + elif os.path.exists(item): + store_path = Session.get_storage_path_by_category(StoragePathCategory.InputData) + config = {"InputUri": upload(item, store_path)} + else: + raise ValueError( + "Invalid input data, supported inputs are OSS, NAS, MaxCompute " + "table or local path." + ) + + return config + + +def get_output_channel_config( + item: Optional[Union[str, FileSystemInputBase]] +) -> Dict[str, str]: + """Get channel config from given job output.""" + + if not isinstance(item, (str, FileSystemInputBase)): + raise ValueError(f"Output data of type {type(item)} is not supported.") + + # OSS URI for output channel will be mounted to directory + # "/ml/output/{ChannelName}/" and the output OSS URI should be a "directory" + def as_oss_dir_uri(uri: str): + folder_uri = uri if uri.endswith("/") else uri + "/" + if folder_uri != uri: + logger.warning( + f"This output URI {uri} is not in the format of a folder path, " + f"system will automatically use {folder_uri} instead." + ) + return folder_uri + + if isinstance(item, FileSystemInputBase): + config = {"OutputUri": item.to_input_uri()} + elif is_oss_uri(item): + config = {"OutputUri": as_oss_dir_uri(item)} + elif is_filesystem_uri(item) or is_odps_table_uri(item): + config = {"OutputUri": item} + elif is_dataset_id(item): + config = {"DatasetId": item} + else: + raise ValueError( + "Invalid output data, supported inputs are OSS, NAS, MaxCompute table." + ) + + return config + + +class Processor(object): + def __init__( + self, + image_uri: str, + command: str, + source_dir: Optional[str] = None, + job_type: str = JobType.PyTorchJob, + parameters: Optional[Dict[str, Any]] = None, + max_run_time: Optional[int] = None, + base_job_name: Optional[str] = None, + output_path: Optional[str] = None, + instance_type: Optional[str] = None, + instance_count: Optional[int] = None, + user_vpc_config: Optional[UserVpcConfig] = None, + session: Optional[Session] = None, + ): + """Processor constructor. + + Args: + image_uri (str): The image used in the job. It can be an image + provided by PAI or a user customized image. To view the images provided + by PAI, please refer to the document: + https://help.aliyun.com/document_detail/202834.htm. + command (str): The command used to run the job. + source_dir (str, optional): The local source code directory used in the + job. The directory will be packaged and uploaded to an OSS + bucket, then downloaded to the `/ml/usercode` directory in the + job container. If there is a `requirements.txt` file in the source code + directory, the corresponding dependencies will be installed before the + script runs. + + If 'git_config' is provided, 'source_dir' should be a relative location + to a directory in the Git repo. With the following GitHub repo directory + structure: + + .. code:: + + |----- README.md + |----- src + |----- train.py + |----- test.py + + if you need 'src' directory as the source code directory, you can assign + source_dir='./src/'. + + code_dir (dict, optional): The "code_dir" object holds the configuration + details for the code location on OSS.If 'code_dir' is provided, + Processor will use it directly and ignore 'source_dir' as well as 'git_config'. + + Example:: + + { + "LocationValue": { + "Bucket": "pai-quickstart-predeploy-hangzhou", + "Key": "/tmp/mock_llm_evaluation/", + "Endpoint": "oss-cn-hangzhou.aliyuncs.com" + }, + "LocationType": "oss" + } + job_type (str): The type of job, which can be TFJob, PyTorchJob, XGBoostJob, + etc. Default value is PyTorchJob. + parameters (dict, optional): A dictionary that represents the + parameters used in the job. The parameters will be + stored in the `/ml/input/config/hyperparameters.json` as a JSON + dictionary in the container. + max_run_time (int, optional): The maximum time in seconds that the + job can run. The job will be terminated after the time is + reached (Default None). + base_job_name (str, optional): The base name used to generate the + job name. + output_path (str, optional): An OSS URI to store the outputs of the + jobs. If not provided, an OSS URI will be generated using the default + OSS bucket in the session. When the `estimator.fit` method is called, + a specific OSS URI under the output_path for each channel is generated + and mounted to the container. + + A completed container directory structure example:: + + /ml + |-- usercode // User source code directory. + | |-- requirements.txt + | `-- train.py + |-- input // Job input + | `-- config + | |-- hyperparameters.json // Hyperparameters in JSON + | | // dictionary format for the + | | // Job + | | + | `-- data // Job input channels + | | // `/ml/input/data/` is a input + | | // channel, and the directory + | | // name is the channel name. + | | // Each directory under the + | |-- test-data + | | `-- test.csv + | `-- train-data + | `-- train.csv + `-- output // Job output channels. + | // Each directory under the + | // `/ml/output/` is an output + | // channel, and the directory + | // name is the channel name. + `-- model + `-- checkpoints + + instance_type (str): The machine instance type used to run the job. + To view the supported machine instance types, please refer to the + document: + https://help.aliyun.com/document_detail/171758.htm#section-55y-4tq-84y. + If the instance_type is "local", the job is executed locally + using docker. + instance_count (int): The number of machines used to run the job. + user_vpc_config (:class:`pai.estimator.UserVpcConfig`, optional): The VPC + configuration used to enable the job instance to connect to the + specified user VPC. If provided, an Elastic Network Interface (ENI) will + be created and attached to the job instance, allowing the + instance to access the resources within the specified VPC. Default to + None. + + session (Session, optional): A PAI session instance used for communicating + with PAI service. + + """ + self.image_uri = image_uri + self.command = command + self.source_dir = source_dir + self.job_type = job_type or JobType.PyTorchJob + self.parameters = parameters or dict() + self.max_run_time = max_run_time + + self.base_job_name = base_job_name + self.output_path = output_path + + self.instance_type = instance_type + self.instance_count = instance_count or 1 + self.user_vpc_config = user_vpc_config + self.session = session or get_default_session() + + self._latest_job = None + self._jobs = [] + + self._input_channel_definitions = None + self._output_channel_definitions = None + + def run( + self, + inputs: Dict[str, Any] = None, + outputs: Dict[str, Any] = None, + wait: bool = True, + show_logs=True, + ): + """Submit a job with the given input and output channels. + + Args: + inputs (Dict[str, Any]): A dictionary representing the input data for the + job. Each key/value pair in the dictionary is an input channel, + the key is the channel name, and the value is the input data. The input + data can be an OSS URI or a NAS URI object and will be mounted to the + `/ml/input/data/{channel_name}` directory in the job container. + outputs (Dict[str, Any]): A dictionary representing the output data for the + job. Each key/value pair in the dictionary is an output channel, + the key is the channel name, and the value is the output path. The output + path can be an OSS URI or a NAS URI object and will be mounted to the + `/ml/outputs/data/{channel_name}` directory in the job container. + wait (bool): Specifies whether to block until the training job is completed, + either succeeded, failed, or stopped. (Default True). + show_logs (bool): Specifies whether to show the logs produced by the + job (Default True). + Raises: + UnExpectedStatusException: If the job fails. + + """ + inputs = inputs or dict() + outputs = outputs or dict() + job_name = self._gen_job_display_name() + + job = self._fit(inputs=inputs, outputs=outputs, job_name=job_name) + self._latest_job = job + + if wait: + self.wait(show_logs=show_logs) + + def _gen_job_display_name(self, job_name=None): + """Generate job display name.""" + if job_name: + return job_name + ts = datetime.now().strftime("%Y%m%d_%H%M%S") + return "{}_{}".format(self.base_job_name or "processing_job", ts) + + def _build_algorithm_spec(self, code_input) -> Dict[str, Any]: + """Build a temporary AlgorithmSpec used for submitting the Job.""" + command = [ + "/bin/sh", + "-c", + self.command, + ] + algo_spec = { + "Command": command, + "Image": self.image_uri, + "JobType": self.job_type, + "CodeDir": code_input, + "InputChannels": self._input_channel_definitions or None, + "OutputChannels": self._output_channel_definitions or None, + } + + return algo_spec + + def _fit( + self, job_name, inputs: Dict[str, Any] = None, outputs: Dict[str, Any] = None + ): + + output_path = self._get_job_base_output_path(job_name) + upload_path = Session.get_storage_path_by_category( + StoragePathCategory.ProcessingSrc, to_plain_text(job_name) + ) + + input_configs = self._build_input_data_configs(inputs) + output_configs = self._build_output_data_configs(output_path, outputs) + + algo_spec = self._build_algorithm_spec( + code_input=build_code_input(self.source_dir, upload_path), + ) + + job_id = self.session.training_job_api.create( + instance_count=self.instance_count, + instance_type=self.instance_type, + job_name=job_name, + hyperparameters=self.parameters, + max_running_in_seconds=self.max_run_time, + input_channels=input_configs, + output_channels=output_configs, + algorithm_spec=algo_spec, + user_vpc_config=self.user_vpc_config.to_dict() + if self.user_vpc_config + else None, + ) + job = _Job.get(job_id) + print(f"View the job detail by accessing the console URI: {job.console_uri}") + return job + + def wait(self, show_logs: bool = True): + """Block until the latest job is completed. + + Args: + show_logs(bool): Specifies whether to fetch and print the logs produced by + the job. + + Raises: + RuntimeError: If no job is submitted. + + """ + if not self._latest_job: + raise RuntimeError("Could not find a submitted job.") + self._latest_job.wait(show_logs=show_logs) + + def _get_job_base_output_path(self, job_name: str) -> str: + """Generate the base output path for the job.""" + + bucket_name = self.session.oss_bucket.bucket_name + # replace non-alphanumeric character in job name. + job_name = to_plain_text(job_name) + + if self.output_path: + return os.path.join(self.output_path, f"{job_name}_{random_str(6)}") + + job_output_path = Session.get_storage_path_by_category( + StoragePathCategory.ProcessingJob, f"{job_name}_{random_str(6)}" + ) + return f"oss://{bucket_name}/{job_output_path}" + + def _build_input_data_configs( + self, + inputs: Dict[str, Any] = None, + ) -> List[Dict[str, str]]: + """Build the input data config for jobs.""" + + res = [] + remain_inputs = {} + + if self._input_channel_definitions: + remains = set(inputs.keys()) + for channel in self._input_channel_definitions: + channel_name = channel["Name"] + channel_required = channel["Required"] + channel_config = {"Name": channel_name} + + if channel_name in inputs: + updated_value = get_input_channel_config(inputs[channel_name]) + channel_config.update(updated_value) + res.append(channel_config) + remains.remove(channel_name) + elif channel_required: + raise ValueError( + f"Input channel {channel_name} is required but not provided." + " Please check the input channels definition." + ) + + # follow the rest of user input channels in Processor. + remain_inputs = {channel: inputs[channel] for channel in remains} + + if remains: + logger.warning( + f"Following input channels={list(remains)} are not defined in input" + " channels definition. Please check the input channels definition." + ) + + for name, item in remain_inputs.items(): + channel_config = {"Name": name} + updated_value = get_input_channel_config(item) + channel_config.update(updated_value) + res.append(channel_config) + + return res + + def _build_output_data_configs( + self, + output_path: str, + outputs: Dict[str, Any] = None, + ) -> List[Dict[str, str]]: + """Build the output data config for jobs.""" + + res = [] + + if self._output_channel_definitions: + # we create all channel config no matter whether channel is required or not for backward compatibility. + for channel in self._output_channel_definitions: + channel_name = channel["Name"] + channel_config = {"Name": channel_name} + + if channel_name in outputs: + updated_value = get_output_channel_config(outputs[channel_name]) + channel_config.update(updated_value) + else: + output_uri = posixpath.join(output_path, channel["Name"]) + updated_value = get_output_channel_config(output_uri) + channel_config.update(updated_value) + + res.append(channel_config) + else: + for name, item in outputs.items(): + channel_config = {"Name": name} + updated_value = get_output_channel_config(item) + channel_config.update(updated_value) + + res.append(channel_config) + + return res + + @property + def latest_job(self): + """Return the latest submitted processing job.""" + return self._latest_job + + def get_outputs_data(self) -> Dict[str, str]: + """Show all outputs data paths. + + Returns: + dict[str, str]: A dictionary of all outputs data paths. + """ + if not self._latest_job: + raise RuntimeError("Current no Job for the processor.") + + return { + ch["Name"]: ch["OutputUri"] or ch["DatasetId"] + for ch in self._latest_job.output_channels + } + + def set_input_channel_definitions(self, definitions: List[Dict[str, Any]]): + self._input_channel_definitions = definitions + + def set_output_channel_definitions(self, definitions: List[Dict[str, Any]]): + self._output_channel_definitions = definitions diff --git a/tests/integration/__init__.py b/tests/integration/__init__.py index 5c8c969..f33208b 100644 --- a/tests/integration/__init__.py +++ b/tests/integration/__init__.py @@ -245,3 +245,12 @@ def upload_file(cls, oss_bucket, location, file): bucket_name=oss_bucket.bucket_name, key=key, ) + + @classmethod + def get_oss_uri(cls, oss_bucket, location, file_name=""): + key = location + file_name + + return "oss://{bucket_name}/{key}".format( + bucket_name=oss_bucket.bucket_name, + key=key, + ) diff --git a/tests/integration/test_processor.py b/tests/integration/test_processor.py new file mode 100644 index 0000000..fe7343c --- /dev/null +++ b/tests/integration/test_processor.py @@ -0,0 +1,59 @@ +# Copyright 2024 Alibaba, Inc. or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os + +from pai.image import retrieve +from pai.processor import Processor +from pai.session import get_default_session +from tests.integration import BaseIntegTestCase +from tests.test_data import SCRIPT_DIR_PATH, test_data_dir + + +class TestProcessor(BaseIntegTestCase): + job_output_path = None + + @classmethod + def setUpClass(cls): + super(TestProcessor, cls).setUpClass() + oss_bucket = get_default_session().oss_bucket # type oss2.Bucket + + cls.breast_cancer_test_data_uri = cls.upload_file( + oss_bucket=oss_bucket, + location="sdk-test/test_data/breast_cancer_data/test/", + file=os.path.join(test_data_dir, "breast_cancer_data/test.csv"), + ) + cls.processing_output_uri = cls.get_oss_uri( + oss_bucket=oss_bucket, + location="sdk-test/output/processing/", + ) + + def test_processing_run(self): + image_uri = retrieve("pytorch", framework_version="1.12").image_uri + processor = Processor( + image_uri=image_uri, + source_dir=SCRIPT_DIR_PATH, + command="python main.py --output_path=/ml/output/flag", + instance_type="ecs.c6.large", + base_job_name="processing", + ) + + processor.run( + inputs={"test": self.breast_cancer_test_data_uri}, + outputs={"flag": self.processing_output_uri}, + ) + + success_flag = os.path.join(self.processing_output_uri, "output.txt") + + self.assertIsNotNone(self.is_oss_object_exists(success_flag)) From 487b21248cda0be1863c1d11813b2b25a8e61882 Mon Sep 17 00:00:00 2001 From: yangziming Date: Wed, 24 Jan 2024 18:49:22 +0800 Subject: [PATCH 11/59] misc: disabel warnings in nox integration test and fix nox test.ini.template --- noxfile.py | 5 ++--- tests/integration/test.ini.template | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/noxfile.py b/noxfile.py index 68d4a0d..cbba9d7 100644 --- a/noxfile.py +++ b/noxfile.py @@ -15,7 +15,6 @@ "requirements/lint-requirements.txt", ) - DOC_REQUIREMENTS = os.path.join( _pkg_root, "requirements/doc-requirements.txt", @@ -23,11 +22,11 @@ PASSING_ENVIRONMENTS = { "PAI_TEST_CONFIG": "test.ini", + "PYTHONWARNINGS": "ignore", } - UNIT_TEST_PYTHON_VERSIONS = ["3.6", "3.7", "3.8"] -INTEGRATION_TEST_PYTHON_VERSIONS = ["3.7"] +INTEGRATION_TEST_PYTHON_VERSIONS = ["3.8"] TEST_VENV_BACKEND = os.environ.get("PAI_TEST_VENV_BACKEND", "conda") diff --git a/tests/integration/test.ini.template b/tests/integration/test.ini.template index 6b88738..a8a4953 100644 --- a/tests/integration/test.ini.template +++ b/tests/integration/test.ini.template @@ -3,6 +3,7 @@ access_key_id= access_key_secret= region_id= +workspace_id= # 算法使用的MaxCompute项目 [odps] From 22c68a4b6a7b259d1f8b528e99e81970612963eb Mon Sep 17 00:00:00 2001 From: yangziming Date: Fri, 26 Jan 2024 20:55:56 +0800 Subject: [PATCH 12/59] feat: build evaluation processor for registered model --- pai/api/model.py | 4 + pai/common/configs.py | 55 ++++++++++++ pai/common/consts.py | 3 + pai/estimator.py | 41 +-------- pai/model.py | 144 ++++++++++++++++++++++++++++++++ pai/processor.py | 3 +- tests/integration/test_model.py | 18 ++++ 7 files changed, 227 insertions(+), 41 deletions(-) create mode 100644 pai/common/configs.py diff --git a/pai/api/model.py b/pai/api/model.py index 2f76b7e..33ffcd2 100644 --- a/pai/api/model.py +++ b/pai/api/model.py @@ -125,6 +125,7 @@ def create_version( self, model_id: str, approval_status: str = None, + evaluation_spec: Dict[str, Any] = None, format_type: str = None, framework_type: str = None, inference_spec: Dict[str, Any] = None, @@ -143,6 +144,7 @@ def create_version( request = CreateModelVersionRequest( approval_status=approval_status, + evaluation_spec=evaluation_spec, format_type=format_type, framework_type=framework_type, inference_spec=inference_spec, @@ -223,6 +225,7 @@ def update_version( model_id: str, version: str, approval_status: str = None, + evaluation_spec: Dict[str, Any] = None, inference_spec: Dict[str, Any] = None, metrics: Dict[str, Any] = None, options: str = None, @@ -233,6 +236,7 @@ def update_version( ): request = UpdateModelVersionRequest( approval_status=approval_status, + evaluation_spec=evaluation_spec, inference_spec=inference_spec, metrics=metrics, options=options, diff --git a/pai/common/configs.py b/pai/common/configs.py new file mode 100644 index 0000000..b7b952c --- /dev/null +++ b/pai/common/configs.py @@ -0,0 +1,55 @@ +# Copyright 2024 Alibaba, Inc. or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import List, Optional + + +class UserVpcConfig(object): + """UserVpcConfig is used to give training job access to resources in your VPC.""" + + def __init__( + self, + vpc_id: str, + security_group_id: str, + switch_id: Optional[str] = None, + extended_cidrs: List[str] = None, + ): + """Initialize UserVpcConfig. + + Args: + vpc_id (str): Specifies the ID of the VPC that training job instance + connects to. + security_group_id (str): The ID of the security group that training job + instances belong to. + switch_id (str, optional): The ID of the vSwitch to which the instance + belongs. Defaults to None. + extended_cidrs (List[str], optional): The CIDR blocks configured for the + ENI of the training job instance. If it is not specified, the CIDR block + will be configured as the same as the VPC network segmentation, which + means that the training job instance can access all resources in the + VPC. Defaults to None. + """ + + self.vpc_id = vpc_id + self.security_group_id = security_group_id + self.switch_id = switch_id + self.extended_cidrs = extended_cidrs + + def to_dict(self): + return { + "VpcId": self.vpc_id, + "SecurityGroupId": self.security_group_id, + "SwitchId": self.switch_id, + "ExtendedCIDRs": self.extended_cidrs, + } diff --git a/pai/common/consts.py b/pai/common/consts.py index d37eac4..2cfd3a1 100644 --- a/pai/common/consts.py +++ b/pai/common/consts.py @@ -118,6 +118,9 @@ class StoragePathCategory(object): # For inference InferenceSrc = "inference_src" + # For evaluation + EvaluationSrc = "evaluation_src" + # For training job TrainingSrc = "training_src" TrainingJob = "training_job" diff --git a/pai/estimator.py b/pai/estimator.py index dc9cd44..0952600 100644 --- a/pai/estimator.py +++ b/pai/estimator.py @@ -34,6 +34,7 @@ from .api.base import PaginatedResult from .api.entity_base import EntityBaseMixin from .common import ProviderAlibabaPAI, git_utils +from .common.configs import UserVpcConfig from .common.consts import INSTANCE_TYPE_LOCAL_GPU, FileSystemInputScheme, JobType from .common.docker_utils import ContainerRun, run_container from .common.oss_utils import OssUriObj, download, is_oss_uri, upload @@ -181,46 +182,6 @@ def to_input_uri(self): ) -class UserVpcConfig(object): - """UserVpcConfig is used to give training job access to resources in your VPC.""" - - def __init__( - self, - vpc_id: str, - security_group_id: str, - switch_id: Optional[str] = None, - extended_cidrs: List[str] = None, - ): - """Initialize UserVpcConfig. - - Args: - vpc_id (str): Specifies the ID of the VPC that training job instance - connects to. - security_group_id (str): The ID of the security group that training job - instances belong to. - switch_id (str, optional): The ID of the vSwitch to which the instance - belongs. Defaults to None. - extended_cidrs (List[str], optional): The CIDR blocks configured for the - ENI of the training job instance. If it is not specified, the CIDR block - will be configured as the same as the VPC network segmentation, which - means that the training job instance can access all resources in the - VPC. Defaults to None. - """ - - self.vpc_id = vpc_id - self.security_group_id = security_group_id - self.switch_id = switch_id - self.extended_cidrs = extended_cidrs - - def to_dict(self): - return { - "VpcId": self.vpc_id, - "SecurityGroupId": self.security_group_id, - "SwitchId": self.switch_id, - "ExtendedCIDRs": self.extended_cidrs, - } - - class EstimatorBase(metaclass=ABCMeta): """EstimatorBase is the base class for other Estimator classes, such as Estimator. diff --git a/pai/model.py b/pai/model.py index 70022c3..b290641 100644 --- a/pai/model.py +++ b/pai/model.py @@ -30,6 +30,7 @@ from oss2 import ObjectIterator from .common import git_utils +from .common.configs import UserVpcConfig from .common.consts import INSTANCE_TYPE_LOCAL_GPU, ModelFormat from .common.docker_utils import ContainerRun, run_container from .common.oss_utils import OssUriObj, download, is_oss_uri, upload @@ -1074,6 +1075,7 @@ def register( format_type: Optional[str] = None, framework_type: Optional[str] = None, training_spec: Optional[Dict[str, Any]] = None, + evaluation_spec: Optional[Dict[str, Any]] = None, approval_status: Optional[str] = None, metrics: Optional[Dict[str, Any]] = None, options: Optional[str] = None, @@ -1110,6 +1112,8 @@ def register( "Xflow", "XGBoost". Default to None. training_spec (dict, optional): The training spec of the model version. Usually, it is got from the training job. Default to None. + evaluation_spec (dict, optional): The evaluation spec of the model version. + Usually, it is got from the processing job for evaluation. Default to None. approval_status (str, optional): The approval status of the model version. The value can be "APPROVED", "PENDING". Default to None. metrics (dict, optional): The metrics of the model version. @@ -1157,6 +1161,7 @@ def register( else: model_id = resp.items[0]["ModelId"] + # TODO support to registry model with evaluation spec version_name = self.session.model_api.create_version( model_id=model_id, uri=self.model_data, @@ -1166,6 +1171,7 @@ def register( format_type=format_type, framework_type=framework_type, training_spec=training_spec, + evaluation_spec=evaluation_spec, inference_spec=self.inference_spec.to_dict() if self.inference_spec else None, @@ -1392,6 +1398,7 @@ def __init__( self.uri = self._model_version_info.get("Uri") self.model_version = self._model_version_info.get("VersionName") self.training_spec = self._model_version_info.get("TrainingSpec") + self.evaluation_spec = self._model_version_info.get("EvaluationSpec") self.model_labels = { lb["Key"]: lb["Value"] for lb in self._model_info.get("Labels", []) } @@ -1884,3 +1891,140 @@ def get_estimator_inputs(self) -> Dict[str, str]: } ) return input_channels + + def get_eval_processor( + self, + base_job_name: Optional[str] = None, + output_path: Optional[str] = None, + parameters: Optional[Dict[str, Any]] = None, + max_run_time: Optional[int] = None, + instance_type: Optional[str] = None, + instance_count: Optional[int] = None, + user_vpc_config: Optional[UserVpcConfig] = None, + ): + """Generate a Processor for model evaluation. + + Generate a Processor object from RegisteredModel's evaluation_spec. + + Args: + parameters (dict, optional): A dictionary that represents the + parameters used in the job. Default parameters will + be retrieved from the evaluation spec. + base_job_name (str, optional): The base name used to generate the + job name. If not provided, a default job name will be generated. + output_path (str, optional): An OSS URI to store the outputs of the + jobs. If not provided, an OSS URI will be generated using the default + OSS bucket in the session. When the `estimator.fit` method is called, + a specific OSS URI under the output_path for each channel is generated + and mounted to the container. + max_run_time (int, optional): The maximum time in seconds that the + job can run. The job will be terminated after the time is + reached (Default None). + instance_type (str, optional): The machine instance type used to run the + job. If not provider, the default instance type will be + retrieved from the evaluation spec. To view the supported machine + instance types, please refer to the document: + https://help.aliyun.com/document_detail/171758.htm#section-55y-4tq-84y. + instance_count (int, optional): The number of machines used to run the + job. If not provider, the default instance count will be + retrieved from the evaluation spec. + user_vpc_config (:class:`pai.estimator.UserVpcConfig`, optional): The VPC + configuration used to enable the job instance to connect to the + specified user VPC. If provided, an Elastic Network Interface (ENI) will + be created and attached to the job instance, allowing the + instance to access the resources within the specified VPC. Default to + None. + Returns: + :class:`pai.processor.Processor`: An Processor object. + """ + from .processor import Processor + + eval_spec = self._get_evaluation_spec() + if not eval_spec: + raise ValueError( + "The provided registered model does not contain evaluation spec." + ) + + if "AlgorithmSpec" not in eval_spec: + raise ValueError( + "The provided registered model's evaluation spec does not contain any" + " workload." + ) + workload = eval_spec.get("AlgorithmSpec") + + if not base_job_name: + base_job_name = f"{self.model_name}_eval" if self.model_name else None + + parameters = parameters or dict() + for item in eval_spec.get("HyperParameters"): + name = item["Name"] + value = item["Value"] + if name not in parameters: + parameters[name] = value + + if not max_run_time: + max_run_time = eval_spec.get("Scheduler", {}).get("MaxRunningTimeInSeconds") + + compute_resource = eval_spec.get("ComputeResource") + if compute_resource and (not instance_type or not instance_count): + # If instance_type or instance_count is not provided, use the default + instance_type = instance_type or compute_resource.get("EcsSpec") + instance_count = instance_count or compute_resource.get("EcsCount") + + source_dir = None + code_dir = workload.get("CodeDir") + if code_dir and code_dir.get("LocationType") == "oss": + location = code_dir.get("LocationValue") + oss_path = OssUriObj.from_bucket_key_endpoint( + bucket_name=location.get("Bucket"), + object_key=location.get("Key"), + endpoint=location.get("Endpoint"), + ) + source_dir = oss_path.uri + + processor = Processor( + image_uri=workload.get("Image"), + command=" ".join(workload.get("Command")), + source_dir=source_dir, + parameters=parameters, + max_run_time=max_run_time, + base_job_name=base_job_name, + output_path=output_path, + instance_type=instance_type, + instance_count=instance_count, + user_vpc_config=user_vpc_config, + session=self.session, + ) + processor.set_input_channel_definitions(workload["InputChannels"]) + processor.set_output_channel_definitions(workload["OutputChannels"]) + + return processor + + def get_evaluation_inputs(self) -> Dict[str, Any]: + """Get the Processor's default input channels + + Get the Processor's default input channels from RegisteredModel's + evaluation_spec. + + Returns: + dict[str, str]: A dict of input channels. + """ + if not self.evaluation_spec: + raise ValueError( + "The provided registered model does not contain evaluation spec." + ) + + input_channels = {} + if "InputChannels" in self.evaluation_spec: + for i in self.evaluation_spec["InputChannels"]: + input_channels.update( + { + i["Name"]: i.get("InputUri") or i.get("DatasetId"), + } + ) + + return input_channels + + def _get_evaluation_spec(self): + """Get the evaluation_spec of the registered model.""" + return self.evaluation_spec diff --git a/pai/processor.py b/pai/processor.py index 7e17399..d831f8e 100644 --- a/pai/processor.py +++ b/pai/processor.py @@ -17,6 +17,7 @@ from datetime import datetime from typing import Any, Dict, List, Optional, Union +from .common.configs import UserVpcConfig from .common.consts import DefaultChannelName, JobType, StoragePathCategory from .common.oss_utils import OssUriObj, is_oss_uri, upload from .common.utils import ( @@ -26,7 +27,7 @@ random_str, to_plain_text, ) -from .estimator import FileSystemInputBase, UserVpcConfig +from .estimator import FileSystemInputBase from .estimator import _TrainingJob as _Job from .session import Session, get_default_session diff --git a/tests/integration/test_model.py b/tests/integration/test_model.py index 42898ca..568803b 100644 --- a/tests/integration/test_model.py +++ b/tests/integration/test_model.py @@ -300,6 +300,24 @@ def test_rm_deploy(self): self.assertTrue(res[0]["label"] == "正向") self.assertTrue(res[1]["label"] == "负向") + @pytest.mark.timeout(60 * 10) + @skipUnless( + False, "No available model in prod environment, please run this case manually." + ) + def test_model_evaluation(self): + m = RegisteredModel( + model_name="qwen-7b-chat", + model_version="0.2.5", + model_provider="pai", + ) + self.assertIsNotNone(m.evaluation_spec) + + inputs = m.get_evaluation_inputs() + processor = m.get_eval_processor( + instance_type="ecs.c6.large", + ) + processor.run(inputs=inputs) + class TestInferenceSpec(BaseIntegTestCase): def test_mount_local_source(self): From 67874dc892775b5d99d8a5ee7d634dc2bbf97cf2 Mon Sep 17 00:00:00 2001 From: yangziming Date: Tue, 30 Jan 2024 10:06:49 +0800 Subject: [PATCH 13/59] chore: bump aiworkspace client to v3.0.2 --- .../__init__.py | 2 +- .../client.py | 4240 ++++-- .../models.py | 11586 +++++++++++----- 3 files changed, 11313 insertions(+), 4515 deletions(-) diff --git a/pai/libs/alibabacloud_aiworkspace20210204/__init__.py b/pai/libs/alibabacloud_aiworkspace20210204/__init__.py index b1b276d..e62a836 100644 --- a/pai/libs/alibabacloud_aiworkspace20210204/__init__.py +++ b/pai/libs/alibabacloud_aiworkspace20210204/__init__.py @@ -1 +1 @@ -__version__ = '1.2.10' \ No newline at end of file +__version__ = '3.0.2' \ No newline at end of file diff --git a/pai/libs/alibabacloud_aiworkspace20210204/client.py b/pai/libs/alibabacloud_aiworkspace20210204/client.py index 436ea45..349993b 100644 --- a/pai/libs/alibabacloud_aiworkspace20210204/client.py +++ b/pai/libs/alibabacloud_aiworkspace20210204/client.py @@ -7,24 +7,25 @@ from alibabacloud_tea_openapi import models as open_api_models from alibabacloud_tea_util.client import Client as UtilClient from alibabacloud_endpoint_util.client import Client as EndpointUtilClient -# from alibabacloud_aiworkspace20210204 import models as aiwork_space_20210204_models +from pai.libs.alibabacloud_aiworkspace20210204 import models as aiwork_space_20210204_models from alibabacloud_tea_util import models as util_models from alibabacloud_openapi_util.client import Client as OpenApiUtilClient -from pai.libs.alibabacloud_aiworkspace20210204 import models as aiwork_space_20210204_models class Client(OpenApiClient): """ *\ """ + def __init__( - self, + self, config: open_api_models.Config, ): super().__init__(config) self._endpoint_rule = '' self.check_config(config) - self._endpoint = self.get_endpoint('aiworkspace', self._region_id, self._endpoint_rule, self._network, self._suffix, self._endpoint_map, self._endpoint) + self._endpoint = self.get_endpoint('aiworkspace', self._region_id, self._endpoint_rule, self._network, + self._suffix, self._endpoint_map, self._endpoint) def get_endpoint( self, @@ -54,12 +55,16 @@ def add_image_with_options( body['Accessibility'] = request.accessibility if not UtilClient.is_unset(request.description): body['Description'] = request.description + if not UtilClient.is_unset(request.image_id): + body['ImageId'] = request.image_id if not UtilClient.is_unset(request.image_uri): body['ImageUri'] = request.image_uri if not UtilClient.is_unset(request.labels): body['Labels'] = request.labels if not UtilClient.is_unset(request.name): body['Name'] = request.name + if not UtilClient.is_unset(request.size): + body['Size'] = request.size if not UtilClient.is_unset(request.workspace_id): body['WorkspaceId'] = request.workspace_id req = open_api_models.OpenApiRequest( @@ -94,12 +99,16 @@ async def add_image_with_options_async( body['Accessibility'] = request.accessibility if not UtilClient.is_unset(request.description): body['Description'] = request.description + if not UtilClient.is_unset(request.image_id): + body['ImageId'] = request.image_id if not UtilClient.is_unset(request.image_uri): body['ImageUri'] = request.image_uri if not UtilClient.is_unset(request.labels): body['Labels'] = request.labels if not UtilClient.is_unset(request.name): body['Name'] = request.name + if not UtilClient.is_unset(request.size): + body['Size'] = request.size if not UtilClient.is_unset(request.workspace_id): body['WorkspaceId'] = request.workspace_id req = open_api_models.OpenApiRequest( @@ -536,6 +545,82 @@ async def create_code_source_async( headers = {} return await self.create_code_source_with_options_async(request, headers, runtime) + def create_collection_with_options( + self, + request: aiwork_space_20210204_models.CreateCollectionRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.CreateCollectionResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.collection_name): + body['CollectionName'] = request.collection_name + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='CreateCollection', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/collections', + method='POST', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.CreateCollectionResponse(), + self.call_api(params, req, runtime) + ) + + async def create_collection_with_options_async( + self, + request: aiwork_space_20210204_models.CreateCollectionRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.CreateCollectionResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.collection_name): + body['CollectionName'] = request.collection_name + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='CreateCollection', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/collections', + method='POST', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.CreateCollectionResponse(), + await self.call_api_async(params, req, runtime) + ) + + def create_collection( + self, + request: aiwork_space_20210204_models.CreateCollectionRequest, + ) -> aiwork_space_20210204_models.CreateCollectionResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.create_collection_with_options(request, headers, runtime) + + async def create_collection_async( + self, + request: aiwork_space_20210204_models.CreateCollectionRequest, + ) -> aiwork_space_20210204_models.CreateCollectionResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.create_collection_with_options_async(request, headers, runtime) + def create_dataset_with_options( self, request: aiwork_space_20210204_models.CreateDatasetRequest, @@ -560,6 +645,8 @@ def create_dataset_with_options( body['Options'] = request.options if not UtilClient.is_unset(request.property): body['Property'] = request.property + if not UtilClient.is_unset(request.provider): + body['Provider'] = request.provider if not UtilClient.is_unset(request.provider_type): body['ProviderType'] = request.provider_type if not UtilClient.is_unset(request.source_id): @@ -614,6 +701,8 @@ async def create_dataset_with_options_async( body['Options'] = request.options if not UtilClient.is_unset(request.property): body['Property'] = request.property + if not UtilClient.is_unset(request.provider): + body['Provider'] = request.provider if not UtilClient.is_unset(request.provider_type): body['ProviderType'] = request.provider_type if not UtilClient.is_unset(request.source_id): @@ -912,6 +1001,98 @@ async def create_ding_talk_robot_message_async( headers = {} return await self.create_ding_talk_robot_message_with_options_async(request, headers, runtime) + def create_experiment_with_options( + self, + request: aiwork_space_20210204_models.CreateExperimentRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.CreateExperimentResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.accessibility): + body['Accessibility'] = request.accessibility + if not UtilClient.is_unset(request.artifact_uri): + body['ArtifactUri'] = request.artifact_uri + if not UtilClient.is_unset(request.labels): + body['Labels'] = request.labels + if not UtilClient.is_unset(request.name): + body['Name'] = request.name + if not UtilClient.is_unset(request.workspace_id): + body['WorkspaceId'] = request.workspace_id + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='CreateExperiment', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/experiments', + method='POST', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.CreateExperimentResponse(), + self.call_api(params, req, runtime) + ) + + async def create_experiment_with_options_async( + self, + request: aiwork_space_20210204_models.CreateExperimentRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.CreateExperimentResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.accessibility): + body['Accessibility'] = request.accessibility + if not UtilClient.is_unset(request.artifact_uri): + body['ArtifactUri'] = request.artifact_uri + if not UtilClient.is_unset(request.labels): + body['Labels'] = request.labels + if not UtilClient.is_unset(request.name): + body['Name'] = request.name + if not UtilClient.is_unset(request.workspace_id): + body['WorkspaceId'] = request.workspace_id + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='CreateExperiment', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/experiments', + method='POST', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.CreateExperimentResponse(), + await self.call_api_async(params, req, runtime) + ) + + def create_experiment( + self, + request: aiwork_space_20210204_models.CreateExperimentRequest, + ) -> aiwork_space_20210204_models.CreateExperimentResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.create_experiment_with_options(request, headers, runtime) + + async def create_experiment_async( + self, + request: aiwork_space_20210204_models.CreateExperimentRequest, + ) -> aiwork_space_20210204_models.CreateExperimentResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.create_experiment_with_options_async(request, headers, runtime) + def create_member_with_options( self, workspace_id: str, @@ -1004,6 +1185,8 @@ def create_model_with_options( body['Accessibility'] = request.accessibility if not UtilClient.is_unset(request.domain): body['Domain'] = request.domain + if not UtilClient.is_unset(request.extra_info): + body['ExtraInfo'] = request.extra_info if not UtilClient.is_unset(request.labels): body['Labels'] = request.labels if not UtilClient.is_unset(request.model_description): @@ -1012,6 +1195,10 @@ def create_model_with_options( body['ModelDoc'] = request.model_doc if not UtilClient.is_unset(request.model_name): body['ModelName'] = request.model_name + if not UtilClient.is_unset(request.model_type): + body['ModelType'] = request.model_type + if not UtilClient.is_unset(request.order_number): + body['OrderNumber'] = request.order_number if not UtilClient.is_unset(request.origin): body['Origin'] = request.origin if not UtilClient.is_unset(request.task): @@ -1050,6 +1237,8 @@ async def create_model_with_options_async( body['Accessibility'] = request.accessibility if not UtilClient.is_unset(request.domain): body['Domain'] = request.domain + if not UtilClient.is_unset(request.extra_info): + body['ExtraInfo'] = request.extra_info if not UtilClient.is_unset(request.labels): body['Labels'] = request.labels if not UtilClient.is_unset(request.model_description): @@ -1058,6 +1247,10 @@ async def create_model_with_options_async( body['ModelDoc'] = request.model_doc if not UtilClient.is_unset(request.model_name): body['ModelName'] = request.model_name + if not UtilClient.is_unset(request.model_type): + body['ModelType'] = request.model_type + if not UtilClient.is_unset(request.order_number): + body['OrderNumber'] = request.order_number if not UtilClient.is_unset(request.origin): body['Origin'] = request.origin if not UtilClient.is_unset(request.task): @@ -1189,6 +1382,8 @@ def create_model_release_with_options( ) -> aiwork_space_20210204_models.CreateModelReleaseResponse: UtilClient.validate_model(request) body = {} + if not UtilClient.is_unset(request.collections): + body['Collections'] = request.collections if not UtilClient.is_unset(request.target_model_origin): body['TargetModelOrigin'] = request.target_model_origin if not UtilClient.is_unset(request.target_model_provider): @@ -1222,6 +1417,8 @@ async def create_model_release_with_options_async( ) -> aiwork_space_20210204_models.CreateModelReleaseResponse: UtilClient.validate_model(request) body = {} + if not UtilClient.is_unset(request.collections): + body['Collections'] = request.collections if not UtilClient.is_unset(request.target_model_origin): body['TargetModelOrigin'] = request.target_model_origin if not UtilClient.is_unset(request.target_model_provider): @@ -1275,6 +1472,10 @@ def create_model_version_with_options( body = {} if not UtilClient.is_unset(request.approval_status): body['ApprovalStatus'] = request.approval_status + if not UtilClient.is_unset(request.evaluation_spec): + body['EvaluationSpec'] = request.evaluation_spec + if not UtilClient.is_unset(request.extra_info): + body['ExtraInfo'] = request.extra_info if not UtilClient.is_unset(request.format_type): body['FormatType'] = request.format_type if not UtilClient.is_unset(request.framework_type): @@ -1330,6 +1531,10 @@ async def create_model_version_with_options_async( body = {} if not UtilClient.is_unset(request.approval_status): body['ApprovalStatus'] = request.approval_status + if not UtilClient.is_unset(request.evaluation_spec): + body['EvaluationSpec'] = request.evaluation_spec + if not UtilClient.is_unset(request.extra_info): + body['ExtraInfo'] = request.extra_info if not UtilClient.is_unset(request.format_type): body['FormatType'] = request.format_type if not UtilClient.is_unset(request.framework_type): @@ -1474,7 +1679,8 @@ async def create_model_version_labels_async( ) -> aiwork_space_20210204_models.CreateModelVersionLabelsResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.create_model_version_labels_with_options_async(model_id, version_name, request, headers, runtime) + return await self.create_model_version_labels_with_options_async(model_id, version_name, request, headers, + runtime) def create_model_version_release_with_options( self, @@ -1562,7 +1768,8 @@ async def create_model_version_release_async( ) -> aiwork_space_20210204_models.CreateModelVersionReleaseResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.create_model_version_release_with_options_async(model_id, version_name, request, headers, runtime) + return await self.create_model_version_release_with_options_async(model_id, version_name, request, headers, + runtime) def create_product_orders_with_options( self, @@ -1720,19 +1927,37 @@ async def create_service_identity_role_async( headers = {} return await self.create_service_identity_role_with_options_async(request, headers, runtime) - def create_user_with_options( + def create_service_template_with_options( self, + request: aiwork_space_20210204_models.CreateServiceTemplateRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.CreateUserResponse: + ) -> aiwork_space_20210204_models.CreateServiceTemplateResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.inference_spec): + body['InferenceSpec'] = request.inference_spec + if not UtilClient.is_unset(request.labels): + body['Labels'] = request.labels + if not UtilClient.is_unset(request.order_number): + body['OrderNumber'] = request.order_number + if not UtilClient.is_unset(request.provider): + body['Provider'] = request.provider + if not UtilClient.is_unset(request.service_template_description): + body['ServiceTemplateDescription'] = request.service_template_description + if not UtilClient.is_unset(request.service_template_doc): + body['ServiceTemplateDoc'] = request.service_template_doc + if not UtilClient.is_unset(request.service_template_name): + body['ServiceTemplateName'] = request.service_template_name req = open_api_models.OpenApiRequest( - headers=headers + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) ) params = open_api_models.Params( - action='CreateUser', + action='CreateServiceTemplate', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/users', + pathname=f'/api/v1/servicetemplates', method='POST', auth_type='AK', style='ROA', @@ -1740,23 +1965,41 @@ def create_user_with_options( body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.CreateUserResponse(), + aiwork_space_20210204_models.CreateServiceTemplateResponse(), self.call_api(params, req, runtime) ) - async def create_user_with_options_async( + async def create_service_template_with_options_async( self, + request: aiwork_space_20210204_models.CreateServiceTemplateRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.CreateUserResponse: + ) -> aiwork_space_20210204_models.CreateServiceTemplateResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.inference_spec): + body['InferenceSpec'] = request.inference_spec + if not UtilClient.is_unset(request.labels): + body['Labels'] = request.labels + if not UtilClient.is_unset(request.order_number): + body['OrderNumber'] = request.order_number + if not UtilClient.is_unset(request.provider): + body['Provider'] = request.provider + if not UtilClient.is_unset(request.service_template_description): + body['ServiceTemplateDescription'] = request.service_template_description + if not UtilClient.is_unset(request.service_template_doc): + body['ServiceTemplateDoc'] = request.service_template_doc + if not UtilClient.is_unset(request.service_template_name): + body['ServiceTemplateName'] = request.service_template_name req = open_api_models.OpenApiRequest( - headers=headers + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) ) params = open_api_models.Params( - action='CreateUser', + action='CreateServiceTemplate', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/users', + pathname=f'/api/v1/servicetemplates', method='POST', auth_type='AK', style='ROA', @@ -1764,45 +2007,46 @@ async def create_user_with_options_async( body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.CreateUserResponse(), + aiwork_space_20210204_models.CreateServiceTemplateResponse(), await self.call_api_async(params, req, runtime) ) - def create_user(self) -> aiwork_space_20210204_models.CreateUserResponse: + def create_service_template( + self, + request: aiwork_space_20210204_models.CreateServiceTemplateRequest, + ) -> aiwork_space_20210204_models.CreateServiceTemplateResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.create_user_with_options(headers, runtime) + return self.create_service_template_with_options(request, headers, runtime) - async def create_user_async(self) -> aiwork_space_20210204_models.CreateUserResponse: + async def create_service_template_async( + self, + request: aiwork_space_20210204_models.CreateServiceTemplateRequest, + ) -> aiwork_space_20210204_models.CreateServiceTemplateResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.create_user_with_options_async(headers, runtime) + return await self.create_service_template_with_options_async(request, headers, runtime) - def create_workspace_with_options( + def create_service_template_labels_with_options( self, - request: aiwork_space_20210204_models.CreateWorkspaceRequest, + service_template_id: str, + request: aiwork_space_20210204_models.CreateServiceTemplateLabelsRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.CreateWorkspaceResponse: + ) -> aiwork_space_20210204_models.CreateServiceTemplateLabelsResponse: UtilClient.validate_model(request) body = {} - if not UtilClient.is_unset(request.description): - body['Description'] = request.description - if not UtilClient.is_unset(request.display_name): - body['DisplayName'] = request.display_name - if not UtilClient.is_unset(request.env_types): - body['EnvTypes'] = request.env_types - if not UtilClient.is_unset(request.workspace_name): - body['WorkspaceName'] = request.workspace_name + if not UtilClient.is_unset(request.labels): + body['Labels'] = request.labels req = open_api_models.OpenApiRequest( headers=headers, body=OpenApiUtilClient.parse_to_map(body) ) params = open_api_models.Params( - action='CreateWorkspace', + action='CreateServiceTemplateLabels', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/workspaces', + pathname=f'/api/v1/servicetemplates/{OpenApiUtilClient.get_encode_param(service_template_id)}/labels', method='POST', auth_type='AK', style='ROA', @@ -1810,35 +2054,30 @@ def create_workspace_with_options( body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.CreateWorkspaceResponse(), + aiwork_space_20210204_models.CreateServiceTemplateLabelsResponse(), self.call_api(params, req, runtime) ) - async def create_workspace_with_options_async( + async def create_service_template_labels_with_options_async( self, - request: aiwork_space_20210204_models.CreateWorkspaceRequest, + service_template_id: str, + request: aiwork_space_20210204_models.CreateServiceTemplateLabelsRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.CreateWorkspaceResponse: + ) -> aiwork_space_20210204_models.CreateServiceTemplateLabelsResponse: UtilClient.validate_model(request) body = {} - if not UtilClient.is_unset(request.description): - body['Description'] = request.description - if not UtilClient.is_unset(request.display_name): - body['DisplayName'] = request.display_name - if not UtilClient.is_unset(request.env_types): - body['EnvTypes'] = request.env_types - if not UtilClient.is_unset(request.workspace_name): - body['WorkspaceName'] = request.workspace_name + if not UtilClient.is_unset(request.labels): + body['Labels'] = request.labels req = open_api_models.OpenApiRequest( headers=headers, body=OpenApiUtilClient.parse_to_map(body) ) params = open_api_models.Params( - action='CreateWorkspace', + action='CreateServiceTemplateLabels', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/workspaces', + pathname=f'/api/v1/servicetemplates/{OpenApiUtilClient.get_encode_param(service_template_id)}/labels', method='POST', auth_type='AK', style='ROA', @@ -1846,48 +2085,56 @@ async def create_workspace_with_options_async( body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.CreateWorkspaceResponse(), + aiwork_space_20210204_models.CreateServiceTemplateLabelsResponse(), await self.call_api_async(params, req, runtime) ) - def create_workspace( + def create_service_template_labels( self, - request: aiwork_space_20210204_models.CreateWorkspaceRequest, - ) -> aiwork_space_20210204_models.CreateWorkspaceResponse: + service_template_id: str, + request: aiwork_space_20210204_models.CreateServiceTemplateLabelsRequest, + ) -> aiwork_space_20210204_models.CreateServiceTemplateLabelsResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.create_workspace_with_options(request, headers, runtime) + return self.create_service_template_labels_with_options(service_template_id, request, headers, runtime) - async def create_workspace_async( + async def create_service_template_labels_async( self, - request: aiwork_space_20210204_models.CreateWorkspaceRequest, - ) -> aiwork_space_20210204_models.CreateWorkspaceResponse: + service_template_id: str, + request: aiwork_space_20210204_models.CreateServiceTemplateLabelsRequest, + ) -> aiwork_space_20210204_models.CreateServiceTemplateLabelsResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.create_workspace_with_options_async(request, headers, runtime) + return await self.create_service_template_labels_with_options_async(service_template_id, request, headers, + runtime) - def create_workspace_resource_with_options( + def create_trial_with_options( self, - workspace_id: str, - request: aiwork_space_20210204_models.CreateWorkspaceResourceRequest, + request: aiwork_space_20210204_models.CreateTrialRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.CreateWorkspaceResourceResponse: + ) -> aiwork_space_20210204_models.CreateTrialResponse: UtilClient.validate_model(request) body = {} - if not UtilClient.is_unset(request.option): - body['Option'] = request.option - if not UtilClient.is_unset(request.resources): - body['Resources'] = request.resources + if not UtilClient.is_unset(request.experiment_id): + body['ExperimentId'] = request.experiment_id + if not UtilClient.is_unset(request.labels): + body['Labels'] = request.labels + if not UtilClient.is_unset(request.name): + body['Name'] = request.name + if not UtilClient.is_unset(request.source_id): + body['SourceId'] = request.source_id + if not UtilClient.is_unset(request.source_type): + body['SourceType'] = request.source_type req = open_api_models.OpenApiRequest( headers=headers, body=OpenApiUtilClient.parse_to_map(body) ) params = open_api_models.Params( - action='CreateWorkspaceResource', + action='CreateTrial', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/workspaces/{OpenApiUtilClient.get_encode_param(workspace_id)}/resources', + pathname=f'/api/v1/trials', method='POST', auth_type='AK', style='ROA', @@ -1895,32 +2142,37 @@ def create_workspace_resource_with_options( body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.CreateWorkspaceResourceResponse(), + aiwork_space_20210204_models.CreateTrialResponse(), self.call_api(params, req, runtime) ) - async def create_workspace_resource_with_options_async( + async def create_trial_with_options_async( self, - workspace_id: str, - request: aiwork_space_20210204_models.CreateWorkspaceResourceRequest, + request: aiwork_space_20210204_models.CreateTrialRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.CreateWorkspaceResourceResponse: + ) -> aiwork_space_20210204_models.CreateTrialResponse: UtilClient.validate_model(request) body = {} - if not UtilClient.is_unset(request.option): - body['Option'] = request.option - if not UtilClient.is_unset(request.resources): - body['Resources'] = request.resources + if not UtilClient.is_unset(request.experiment_id): + body['ExperimentId'] = request.experiment_id + if not UtilClient.is_unset(request.labels): + body['Labels'] = request.labels + if not UtilClient.is_unset(request.name): + body['Name'] = request.name + if not UtilClient.is_unset(request.source_id): + body['SourceId'] = request.source_id + if not UtilClient.is_unset(request.source_type): + body['SourceType'] = request.source_type req = open_api_models.OpenApiRequest( headers=headers, body=OpenApiUtilClient.parse_to_map(body) ) params = open_api_models.Params( - action='CreateWorkspaceResource', + action='CreateTrial', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/workspaces/{OpenApiUtilClient.get_encode_param(workspace_id)}/resources', + pathname=f'/api/v1/trials', method='POST', auth_type='AK', style='ROA', @@ -1928,34 +2180,262 @@ async def create_workspace_resource_with_options_async( body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.CreateWorkspaceResourceResponse(), + aiwork_space_20210204_models.CreateTrialResponse(), await self.call_api_async(params, req, runtime) ) - def create_workspace_resource( + def create_trial( self, - workspace_id: str, - request: aiwork_space_20210204_models.CreateWorkspaceResourceRequest, - ) -> aiwork_space_20210204_models.CreateWorkspaceResourceResponse: + request: aiwork_space_20210204_models.CreateTrialRequest, + ) -> aiwork_space_20210204_models.CreateTrialResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.create_workspace_resource_with_options(workspace_id, request, headers, runtime) + return self.create_trial_with_options(request, headers, runtime) - async def create_workspace_resource_async( + async def create_trial_async( self, - workspace_id: str, - request: aiwork_space_20210204_models.CreateWorkspaceResourceRequest, - ) -> aiwork_space_20210204_models.CreateWorkspaceResourceResponse: + request: aiwork_space_20210204_models.CreateTrialRequest, + ) -> aiwork_space_20210204_models.CreateTrialResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.create_workspace_resource_with_options_async(workspace_id, request, headers, runtime) + return await self.create_trial_with_options_async(request, headers, runtime) - def delete_code_source_with_options( + def create_user_with_options( self, - code_source_id: str, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.DeleteCodeSourceResponse: + ) -> aiwork_space_20210204_models.CreateUserResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='CreateUser', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/users', + method='POST', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.CreateUserResponse(), + self.call_api(params, req, runtime) + ) + + async def create_user_with_options_async( + self, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.CreateUserResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='CreateUser', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/users', + method='POST', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.CreateUserResponse(), + await self.call_api_async(params, req, runtime) + ) + + def create_user(self) -> aiwork_space_20210204_models.CreateUserResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.create_user_with_options(headers, runtime) + + async def create_user_async(self) -> aiwork_space_20210204_models.CreateUserResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.create_user_with_options_async(headers, runtime) + + def create_workspace_with_options( + self, + request: aiwork_space_20210204_models.CreateWorkspaceRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.CreateWorkspaceResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.description): + body['Description'] = request.description + if not UtilClient.is_unset(request.display_name): + body['DisplayName'] = request.display_name + if not UtilClient.is_unset(request.env_types): + body['EnvTypes'] = request.env_types + if not UtilClient.is_unset(request.workspace_name): + body['WorkspaceName'] = request.workspace_name + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='CreateWorkspace', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/workspaces', + method='POST', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.CreateWorkspaceResponse(), + self.call_api(params, req, runtime) + ) + + async def create_workspace_with_options_async( + self, + request: aiwork_space_20210204_models.CreateWorkspaceRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.CreateWorkspaceResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.description): + body['Description'] = request.description + if not UtilClient.is_unset(request.display_name): + body['DisplayName'] = request.display_name + if not UtilClient.is_unset(request.env_types): + body['EnvTypes'] = request.env_types + if not UtilClient.is_unset(request.workspace_name): + body['WorkspaceName'] = request.workspace_name + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='CreateWorkspace', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/workspaces', + method='POST', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.CreateWorkspaceResponse(), + await self.call_api_async(params, req, runtime) + ) + + def create_workspace( + self, + request: aiwork_space_20210204_models.CreateWorkspaceRequest, + ) -> aiwork_space_20210204_models.CreateWorkspaceResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.create_workspace_with_options(request, headers, runtime) + + async def create_workspace_async( + self, + request: aiwork_space_20210204_models.CreateWorkspaceRequest, + ) -> aiwork_space_20210204_models.CreateWorkspaceResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.create_workspace_with_options_async(request, headers, runtime) + + def create_workspace_resource_with_options( + self, + workspace_id: str, + request: aiwork_space_20210204_models.CreateWorkspaceResourceRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.CreateWorkspaceResourceResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.option): + body['Option'] = request.option + if not UtilClient.is_unset(request.resources): + body['Resources'] = request.resources + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='CreateWorkspaceResource', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/workspaces/{OpenApiUtilClient.get_encode_param(workspace_id)}/resources', + method='POST', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.CreateWorkspaceResourceResponse(), + self.call_api(params, req, runtime) + ) + + async def create_workspace_resource_with_options_async( + self, + workspace_id: str, + request: aiwork_space_20210204_models.CreateWorkspaceResourceRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.CreateWorkspaceResourceResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.option): + body['Option'] = request.option + if not UtilClient.is_unset(request.resources): + body['Resources'] = request.resources + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='CreateWorkspaceResource', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/workspaces/{OpenApiUtilClient.get_encode_param(workspace_id)}/resources', + method='POST', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.CreateWorkspaceResourceResponse(), + await self.call_api_async(params, req, runtime) + ) + + def create_workspace_resource( + self, + workspace_id: str, + request: aiwork_space_20210204_models.CreateWorkspaceResourceRequest, + ) -> aiwork_space_20210204_models.CreateWorkspaceResourceResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.create_workspace_resource_with_options(workspace_id, request, headers, runtime) + + async def create_workspace_resource_async( + self, + workspace_id: str, + request: aiwork_space_20210204_models.CreateWorkspaceResourceRequest, + ) -> aiwork_space_20210204_models.CreateWorkspaceResourceResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.create_workspace_resource_with_options_async(workspace_id, request, headers, runtime) + + def delete_code_source_with_options( + self, + code_source_id: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.DeleteCodeSourceResponse: req = open_api_models.OpenApiRequest( headers=headers ) @@ -2016,21 +2496,20 @@ async def delete_code_source_async( headers = {} return await self.delete_code_source_with_options_async(code_source_id, headers, runtime) - def delete_config_with_options( + def delete_collection_with_options( self, - workspace_id: str, - config_key: str, + collection_name: str, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.DeleteConfigResponse: + ) -> aiwork_space_20210204_models.DeleteCollectionResponse: req = open_api_models.OpenApiRequest( headers=headers ) params = open_api_models.Params( - action='DeleteConfig', + action='DeleteCollection', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/workspaces/{OpenApiUtilClient.get_encode_param(workspace_id)}/configs/{OpenApiUtilClient.get_encode_param(config_key)}', + pathname=f'/api/v1/collections/{OpenApiUtilClient.get_encode_param(collection_name)}', method='DELETE', auth_type='AK', style='ROA', @@ -2038,25 +2517,1007 @@ def delete_config_with_options( body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.DeleteConfigResponse(), + aiwork_space_20210204_models.DeleteCollectionResponse(), self.call_api(params, req, runtime) ) - async def delete_config_with_options_async( + async def delete_collection_with_options_async( self, - workspace_id: str, - config_key: str, + collection_name: str, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.DeleteConfigResponse: + ) -> aiwork_space_20210204_models.DeleteCollectionResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='DeleteCollection', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/collections/{OpenApiUtilClient.get_encode_param(collection_name)}', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.DeleteCollectionResponse(), + await self.call_api_async(params, req, runtime) + ) + + def delete_collection( + self, + collection_name: str, + ) -> aiwork_space_20210204_models.DeleteCollectionResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.delete_collection_with_options(collection_name, headers, runtime) + + async def delete_collection_async( + self, + collection_name: str, + ) -> aiwork_space_20210204_models.DeleteCollectionResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.delete_collection_with_options_async(collection_name, headers, runtime) + + def delete_config_with_options( + self, + workspace_id: str, + config_key: str, + request: aiwork_space_20210204_models.DeleteConfigRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.DeleteConfigResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.labels): + query['Labels'] = request.labels + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='DeleteConfig', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/workspaces/{OpenApiUtilClient.get_encode_param(workspace_id)}/configs/{OpenApiUtilClient.get_encode_param(config_key)}', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.DeleteConfigResponse(), + self.call_api(params, req, runtime) + ) + + async def delete_config_with_options_async( + self, + workspace_id: str, + config_key: str, + request: aiwork_space_20210204_models.DeleteConfigRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.DeleteConfigResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.labels): + query['Labels'] = request.labels + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='DeleteConfig', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/workspaces/{OpenApiUtilClient.get_encode_param(workspace_id)}/configs/{OpenApiUtilClient.get_encode_param(config_key)}', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.DeleteConfigResponse(), + await self.call_api_async(params, req, runtime) + ) + + def delete_config( + self, + workspace_id: str, + config_key: str, + request: aiwork_space_20210204_models.DeleteConfigRequest, + ) -> aiwork_space_20210204_models.DeleteConfigResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.delete_config_with_options(workspace_id, config_key, request, headers, runtime) + + async def delete_config_async( + self, + workspace_id: str, + config_key: str, + request: aiwork_space_20210204_models.DeleteConfigRequest, + ) -> aiwork_space_20210204_models.DeleteConfigResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.delete_config_with_options_async(workspace_id, config_key, request, headers, runtime) + + def delete_dataset_with_options( + self, + dataset_id: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.DeleteDatasetResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='DeleteDataset', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/datasets/{OpenApiUtilClient.get_encode_param(dataset_id)}', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.DeleteDatasetResponse(), + self.call_api(params, req, runtime) + ) + + async def delete_dataset_with_options_async( + self, + dataset_id: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.DeleteDatasetResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='DeleteDataset', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/datasets/{OpenApiUtilClient.get_encode_param(dataset_id)}', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.DeleteDatasetResponse(), + await self.call_api_async(params, req, runtime) + ) + + def delete_dataset( + self, + dataset_id: str, + ) -> aiwork_space_20210204_models.DeleteDatasetResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.delete_dataset_with_options(dataset_id, headers, runtime) + + async def delete_dataset_async( + self, + dataset_id: str, + ) -> aiwork_space_20210204_models.DeleteDatasetResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.delete_dataset_with_options_async(dataset_id, headers, runtime) + + def delete_dataset_labels_with_options( + self, + dataset_id: str, + request: aiwork_space_20210204_models.DeleteDatasetLabelsRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.DeleteDatasetLabelsResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.keys): + query['Keys'] = request.keys + if not UtilClient.is_unset(request.label_keys): + query['LabelKeys'] = request.label_keys + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='DeleteDatasetLabels', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/datasets/{OpenApiUtilClient.get_encode_param(dataset_id)}/labels', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.DeleteDatasetLabelsResponse(), + self.call_api(params, req, runtime) + ) + + async def delete_dataset_labels_with_options_async( + self, + dataset_id: str, + request: aiwork_space_20210204_models.DeleteDatasetLabelsRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.DeleteDatasetLabelsResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.keys): + query['Keys'] = request.keys + if not UtilClient.is_unset(request.label_keys): + query['LabelKeys'] = request.label_keys + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='DeleteDatasetLabels', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/datasets/{OpenApiUtilClient.get_encode_param(dataset_id)}/labels', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.DeleteDatasetLabelsResponse(), + await self.call_api_async(params, req, runtime) + ) + + def delete_dataset_labels( + self, + dataset_id: str, + request: aiwork_space_20210204_models.DeleteDatasetLabelsRequest, + ) -> aiwork_space_20210204_models.DeleteDatasetLabelsResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.delete_dataset_labels_with_options(dataset_id, request, headers, runtime) + + async def delete_dataset_labels_async( + self, + dataset_id: str, + request: aiwork_space_20210204_models.DeleteDatasetLabelsRequest, + ) -> aiwork_space_20210204_models.DeleteDatasetLabelsResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.delete_dataset_labels_with_options_async(dataset_id, request, headers, runtime) + + def delete_experiment_with_options( + self, + experiment_id: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.DeleteExperimentResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='DeleteExperiment', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/experiments/{OpenApiUtilClient.get_encode_param(experiment_id)}', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.DeleteExperimentResponse(), + self.call_api(params, req, runtime) + ) + + async def delete_experiment_with_options_async( + self, + experiment_id: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.DeleteExperimentResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='DeleteExperiment', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/experiments/{OpenApiUtilClient.get_encode_param(experiment_id)}', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.DeleteExperimentResponse(), + await self.call_api_async(params, req, runtime) + ) + + def delete_experiment( + self, + experiment_id: str, + ) -> aiwork_space_20210204_models.DeleteExperimentResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.delete_experiment_with_options(experiment_id, headers, runtime) + + async def delete_experiment_async( + self, + experiment_id: str, + ) -> aiwork_space_20210204_models.DeleteExperimentResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.delete_experiment_with_options_async(experiment_id, headers, runtime) + + def delete_experiment_label_with_options( + self, + experiment_id: str, + key: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.DeleteExperimentLabelResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='DeleteExperimentLabel', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/experiments/{OpenApiUtilClient.get_encode_param(experiment_id)}/labels/{OpenApiUtilClient.get_encode_param(key)}', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.DeleteExperimentLabelResponse(), + self.call_api(params, req, runtime) + ) + + async def delete_experiment_label_with_options_async( + self, + experiment_id: str, + key: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.DeleteExperimentLabelResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='DeleteExperimentLabel', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/experiments/{OpenApiUtilClient.get_encode_param(experiment_id)}/labels/{OpenApiUtilClient.get_encode_param(key)}', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.DeleteExperimentLabelResponse(), + await self.call_api_async(params, req, runtime) + ) + + def delete_experiment_label( + self, + experiment_id: str, + key: str, + ) -> aiwork_space_20210204_models.DeleteExperimentLabelResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.delete_experiment_label_with_options(experiment_id, key, headers, runtime) + + async def delete_experiment_label_async( + self, + experiment_id: str, + key: str, + ) -> aiwork_space_20210204_models.DeleteExperimentLabelResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.delete_experiment_label_with_options_async(experiment_id, key, headers, runtime) + + def delete_members_with_options( + self, + workspace_id: str, + request: aiwork_space_20210204_models.DeleteMembersRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.DeleteMembersResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.member_ids): + query['MemberIds'] = request.member_ids + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='DeleteMembers', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/workspaces/{OpenApiUtilClient.get_encode_param(workspace_id)}/members', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.DeleteMembersResponse(), + self.call_api(params, req, runtime) + ) + + async def delete_members_with_options_async( + self, + workspace_id: str, + request: aiwork_space_20210204_models.DeleteMembersRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.DeleteMembersResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.member_ids): + query['MemberIds'] = request.member_ids + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='DeleteMembers', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/workspaces/{OpenApiUtilClient.get_encode_param(workspace_id)}/members', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.DeleteMembersResponse(), + await self.call_api_async(params, req, runtime) + ) + + def delete_members( + self, + workspace_id: str, + request: aiwork_space_20210204_models.DeleteMembersRequest, + ) -> aiwork_space_20210204_models.DeleteMembersResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.delete_members_with_options(workspace_id, request, headers, runtime) + + async def delete_members_async( + self, + workspace_id: str, + request: aiwork_space_20210204_models.DeleteMembersRequest, + ) -> aiwork_space_20210204_models.DeleteMembersResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.delete_members_with_options_async(workspace_id, request, headers, runtime) + + def delete_model_with_options( + self, + model_id: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.DeleteModelResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='DeleteModel', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/models/{OpenApiUtilClient.get_encode_param(model_id)}', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.DeleteModelResponse(), + self.call_api(params, req, runtime) + ) + + async def delete_model_with_options_async( + self, + model_id: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.DeleteModelResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='DeleteModel', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/models/{OpenApiUtilClient.get_encode_param(model_id)}', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.DeleteModelResponse(), + await self.call_api_async(params, req, runtime) + ) + + def delete_model( + self, + model_id: str, + ) -> aiwork_space_20210204_models.DeleteModelResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.delete_model_with_options(model_id, headers, runtime) + + async def delete_model_async( + self, + model_id: str, + ) -> aiwork_space_20210204_models.DeleteModelResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.delete_model_with_options_async(model_id, headers, runtime) + + def delete_model_domain_with_options( + self, + model_domain_id: str, + request: aiwork_space_20210204_models.DeleteModelDomainRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.DeleteModelDomainResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.model_task_ids): + query['ModelTaskIds'] = request.model_task_ids + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='DeleteModelDomain', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/modeldomains/{OpenApiUtilClient.get_encode_param(model_domain_id)}', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.DeleteModelDomainResponse(), + self.call_api(params, req, runtime) + ) + + async def delete_model_domain_with_options_async( + self, + model_domain_id: str, + request: aiwork_space_20210204_models.DeleteModelDomainRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.DeleteModelDomainResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.model_task_ids): + query['ModelTaskIds'] = request.model_task_ids + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='DeleteModelDomain', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/modeldomains/{OpenApiUtilClient.get_encode_param(model_domain_id)}', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.DeleteModelDomainResponse(), + await self.call_api_async(params, req, runtime) + ) + + def delete_model_domain( + self, + model_domain_id: str, + request: aiwork_space_20210204_models.DeleteModelDomainRequest, + ) -> aiwork_space_20210204_models.DeleteModelDomainResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.delete_model_domain_with_options(model_domain_id, request, headers, runtime) + + async def delete_model_domain_async( + self, + model_domain_id: str, + request: aiwork_space_20210204_models.DeleteModelDomainRequest, + ) -> aiwork_space_20210204_models.DeleteModelDomainResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.delete_model_domain_with_options_async(model_domain_id, request, headers, runtime) + + def delete_model_labels_with_options( + self, + model_id: str, + request: aiwork_space_20210204_models.DeleteModelLabelsRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.DeleteModelLabelsResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.keys): + query['Keys'] = request.keys + if not UtilClient.is_unset(request.label_keys): + query['LabelKeys'] = request.label_keys + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='DeleteModelLabels', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/models/{OpenApiUtilClient.get_encode_param(model_id)}/labels', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.DeleteModelLabelsResponse(), + self.call_api(params, req, runtime) + ) + + async def delete_model_labels_with_options_async( + self, + model_id: str, + request: aiwork_space_20210204_models.DeleteModelLabelsRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.DeleteModelLabelsResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.keys): + query['Keys'] = request.keys + if not UtilClient.is_unset(request.label_keys): + query['LabelKeys'] = request.label_keys + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='DeleteModelLabels', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/models/{OpenApiUtilClient.get_encode_param(model_id)}/labels', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.DeleteModelLabelsResponse(), + await self.call_api_async(params, req, runtime) + ) + + def delete_model_labels( + self, + model_id: str, + request: aiwork_space_20210204_models.DeleteModelLabelsRequest, + ) -> aiwork_space_20210204_models.DeleteModelLabelsResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.delete_model_labels_with_options(model_id, request, headers, runtime) + + async def delete_model_labels_async( + self, + model_id: str, + request: aiwork_space_20210204_models.DeleteModelLabelsRequest, + ) -> aiwork_space_20210204_models.DeleteModelLabelsResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.delete_model_labels_with_options_async(model_id, request, headers, runtime) + + def delete_model_version_with_options( + self, + model_id: str, + version_name: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.DeleteModelVersionResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='DeleteModelVersion', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/models/{OpenApiUtilClient.get_encode_param(model_id)}/versions/{OpenApiUtilClient.get_encode_param(version_name)}', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.DeleteModelVersionResponse(), + self.call_api(params, req, runtime) + ) + + async def delete_model_version_with_options_async( + self, + model_id: str, + version_name: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.DeleteModelVersionResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='DeleteModelVersion', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/models/{OpenApiUtilClient.get_encode_param(model_id)}/versions/{OpenApiUtilClient.get_encode_param(version_name)}', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.DeleteModelVersionResponse(), + await self.call_api_async(params, req, runtime) + ) + + def delete_model_version( + self, + model_id: str, + version_name: str, + ) -> aiwork_space_20210204_models.DeleteModelVersionResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.delete_model_version_with_options(model_id, version_name, headers, runtime) + + async def delete_model_version_async( + self, + model_id: str, + version_name: str, + ) -> aiwork_space_20210204_models.DeleteModelVersionResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.delete_model_version_with_options_async(model_id, version_name, headers, runtime) + + def delete_model_version_labels_with_options( + self, + model_id: str, + version_name: str, + request: aiwork_space_20210204_models.DeleteModelVersionLabelsRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.DeleteModelVersionLabelsResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.keys): + query['Keys'] = request.keys + if not UtilClient.is_unset(request.label_keys): + query['LabelKeys'] = request.label_keys + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='DeleteModelVersionLabels', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/models/{OpenApiUtilClient.get_encode_param(model_id)}/versions/{OpenApiUtilClient.get_encode_param(version_name)}/labels', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.DeleteModelVersionLabelsResponse(), + self.call_api(params, req, runtime) + ) + + async def delete_model_version_labels_with_options_async( + self, + model_id: str, + version_name: str, + request: aiwork_space_20210204_models.DeleteModelVersionLabelsRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.DeleteModelVersionLabelsResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.keys): + query['Keys'] = request.keys + if not UtilClient.is_unset(request.label_keys): + query['LabelKeys'] = request.label_keys + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='DeleteModelVersionLabels', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/models/{OpenApiUtilClient.get_encode_param(model_id)}/versions/{OpenApiUtilClient.get_encode_param(version_name)}/labels', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.DeleteModelVersionLabelsResponse(), + await self.call_api_async(params, req, runtime) + ) + + def delete_model_version_labels( + self, + model_id: str, + version_name: str, + request: aiwork_space_20210204_models.DeleteModelVersionLabelsRequest, + ) -> aiwork_space_20210204_models.DeleteModelVersionLabelsResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.delete_model_version_labels_with_options(model_id, version_name, request, headers, runtime) + + async def delete_model_version_labels_async( + self, + model_id: str, + version_name: str, + request: aiwork_space_20210204_models.DeleteModelVersionLabelsRequest, + ) -> aiwork_space_20210204_models.DeleteModelVersionLabelsResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.delete_model_version_labels_with_options_async(model_id, version_name, request, headers, + runtime) + + def delete_service_template_with_options( + self, + service_template_id: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.DeleteServiceTemplateResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='DeleteServiceTemplate', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/servicetemplates/{OpenApiUtilClient.get_encode_param(service_template_id)}', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.DeleteServiceTemplateResponse(), + self.call_api(params, req, runtime) + ) + + async def delete_service_template_with_options_async( + self, + service_template_id: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.DeleteServiceTemplateResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='DeleteServiceTemplate', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/servicetemplates/{OpenApiUtilClient.get_encode_param(service_template_id)}', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.DeleteServiceTemplateResponse(), + await self.call_api_async(params, req, runtime) + ) + + def delete_service_template( + self, + service_template_id: str, + ) -> aiwork_space_20210204_models.DeleteServiceTemplateResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.delete_service_template_with_options(service_template_id, headers, runtime) + + async def delete_service_template_async( + self, + service_template_id: str, + ) -> aiwork_space_20210204_models.DeleteServiceTemplateResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.delete_service_template_with_options_async(service_template_id, headers, runtime) + + def delete_service_template_labels_with_options( + self, + service_template_id: str, + request: aiwork_space_20210204_models.DeleteServiceTemplateLabelsRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.DeleteServiceTemplateLabelsResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.label_keys): + query['LabelKeys'] = request.label_keys + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='DeleteServiceTemplateLabels', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/servicetemplates/{OpenApiUtilClient.get_encode_param(service_template_id)}/labels', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.DeleteServiceTemplateLabelsResponse(), + self.call_api(params, req, runtime) + ) + + async def delete_service_template_labels_with_options_async( + self, + service_template_id: str, + request: aiwork_space_20210204_models.DeleteServiceTemplateLabelsRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.DeleteServiceTemplateLabelsResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.label_keys): + query['LabelKeys'] = request.label_keys req = open_api_models.OpenApiRequest( - headers=headers + headers=headers, + query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='DeleteConfig', + action='DeleteServiceTemplateLabels', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/workspaces/{OpenApiUtilClient.get_encode_param(workspace_id)}/configs/{OpenApiUtilClient.get_encode_param(config_key)}', + pathname=f'/api/v1/servicetemplates/{OpenApiUtilClient.get_encode_param(service_template_id)}/labels', method='DELETE', auth_type='AK', style='ROA', @@ -2064,42 +3525,49 @@ async def delete_config_with_options_async( body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.DeleteConfigResponse(), + aiwork_space_20210204_models.DeleteServiceTemplateLabelsResponse(), await self.call_api_async(params, req, runtime) ) - def delete_config( + def delete_service_template_labels( self, - workspace_id: str, - config_key: str, - ) -> aiwork_space_20210204_models.DeleteConfigResponse: + service_template_id: str, + request: aiwork_space_20210204_models.DeleteServiceTemplateLabelsRequest, + ) -> aiwork_space_20210204_models.DeleteServiceTemplateLabelsResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.delete_config_with_options(workspace_id, config_key, headers, runtime) + return self.delete_service_template_labels_with_options(service_template_id, request, headers, runtime) - async def delete_config_async( + async def delete_service_template_labels_async( self, - workspace_id: str, - config_key: str, - ) -> aiwork_space_20210204_models.DeleteConfigResponse: + service_template_id: str, + request: aiwork_space_20210204_models.DeleteServiceTemplateLabelsRequest, + ) -> aiwork_space_20210204_models.DeleteServiceTemplateLabelsResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.delete_config_with_options_async(workspace_id, config_key, headers, runtime) + return await self.delete_service_template_labels_with_options_async(service_template_id, request, headers, + runtime) - def delete_dataset_with_options( + def delete_user_config_with_options( self, - dataset_id: str, + category_name: str, + request: aiwork_space_20210204_models.DeleteUserConfigRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.DeleteDatasetResponse: + ) -> aiwork_space_20210204_models.DeleteUserConfigResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.config_key): + query['ConfigKey'] = request.config_key req = open_api_models.OpenApiRequest( - headers=headers + headers=headers, + query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='DeleteDataset', + action='DeleteUserConfig', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/datasets/{OpenApiUtilClient.get_encode_param(dataset_id)}', + pathname=f'/api/v1/userconfigs/{OpenApiUtilClient.get_encode_param(category_name)}', method='DELETE', auth_type='AK', style='ROA', @@ -2107,24 +3575,30 @@ def delete_dataset_with_options( body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.DeleteDatasetResponse(), + aiwork_space_20210204_models.DeleteUserConfigResponse(), self.call_api(params, req, runtime) ) - async def delete_dataset_with_options_async( + async def delete_user_config_with_options_async( self, - dataset_id: str, + category_name: str, + request: aiwork_space_20210204_models.DeleteUserConfigRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.DeleteDatasetResponse: + ) -> aiwork_space_20210204_models.DeleteUserConfigResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.config_key): + query['ConfigKey'] = request.config_key req = open_api_models.OpenApiRequest( - headers=headers + headers=headers, + query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='DeleteDataset', + action='DeleteUserConfig', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/datasets/{OpenApiUtilClient.get_encode_param(dataset_id)}', + pathname=f'/api/v1/userconfigs/{OpenApiUtilClient.get_encode_param(category_name)}', method='DELETE', auth_type='AK', style='ROA', @@ -2132,48 +3606,42 @@ async def delete_dataset_with_options_async( body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.DeleteDatasetResponse(), + aiwork_space_20210204_models.DeleteUserConfigResponse(), await self.call_api_async(params, req, runtime) ) - def delete_dataset( + def delete_user_config( self, - dataset_id: str, - ) -> aiwork_space_20210204_models.DeleteDatasetResponse: + category_name: str, + request: aiwork_space_20210204_models.DeleteUserConfigRequest, + ) -> aiwork_space_20210204_models.DeleteUserConfigResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.delete_dataset_with_options(dataset_id, headers, runtime) + return self.delete_user_config_with_options(category_name, request, headers, runtime) - async def delete_dataset_async( + async def delete_user_config_async( self, - dataset_id: str, - ) -> aiwork_space_20210204_models.DeleteDatasetResponse: + category_name: str, + request: aiwork_space_20210204_models.DeleteUserConfigRequest, + ) -> aiwork_space_20210204_models.DeleteUserConfigResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.delete_dataset_with_options_async(dataset_id, headers, runtime) + return await self.delete_user_config_with_options_async(category_name, request, headers, runtime) - def delete_dataset_labels_with_options( + def delete_workspace_with_options( self, - dataset_id: str, - request: aiwork_space_20210204_models.DeleteDatasetLabelsRequest, + workspace_id: str, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.DeleteDatasetLabelsResponse: - UtilClient.validate_model(request) - query = {} - if not UtilClient.is_unset(request.keys): - query['Keys'] = request.keys - if not UtilClient.is_unset(request.label_keys): - query['LabelKeys'] = request.label_keys + ) -> aiwork_space_20210204_models.DeleteWorkspaceResponse: req = open_api_models.OpenApiRequest( - headers=headers, - query=OpenApiUtilClient.query(query) + headers=headers ) params = open_api_models.Params( - action='DeleteDatasetLabels', + action='DeleteWorkspace', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/datasets/{OpenApiUtilClient.get_encode_param(dataset_id)}/labels', + pathname=f'/api/v1/workspaces/{OpenApiUtilClient.get_encode_param(workspace_id)}', method='DELETE', auth_type='AK', style='ROA', @@ -2181,32 +3649,24 @@ def delete_dataset_labels_with_options( body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.DeleteDatasetLabelsResponse(), + aiwork_space_20210204_models.DeleteWorkspaceResponse(), self.call_api(params, req, runtime) ) - async def delete_dataset_labels_with_options_async( + async def delete_workspace_with_options_async( self, - dataset_id: str, - request: aiwork_space_20210204_models.DeleteDatasetLabelsRequest, + workspace_id: str, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.DeleteDatasetLabelsResponse: - UtilClient.validate_model(request) - query = {} - if not UtilClient.is_unset(request.keys): - query['Keys'] = request.keys - if not UtilClient.is_unset(request.label_keys): - query['LabelKeys'] = request.label_keys + ) -> aiwork_space_20210204_models.DeleteWorkspaceResponse: req = open_api_models.OpenApiRequest( - headers=headers, - query=OpenApiUtilClient.query(query) + headers=headers ) params = open_api_models.Params( - action='DeleteDatasetLabels', + action='DeleteWorkspace', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/datasets/{OpenApiUtilClient.get_encode_param(dataset_id)}/labels', + pathname=f'/api/v1/workspaces/{OpenApiUtilClient.get_encode_param(workspace_id)}', method='DELETE', auth_type='AK', style='ROA', @@ -2214,48 +3674,56 @@ async def delete_dataset_labels_with_options_async( body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.DeleteDatasetLabelsResponse(), + aiwork_space_20210204_models.DeleteWorkspaceResponse(), await self.call_api_async(params, req, runtime) ) - def delete_dataset_labels( + def delete_workspace( self, - dataset_id: str, - request: aiwork_space_20210204_models.DeleteDatasetLabelsRequest, - ) -> aiwork_space_20210204_models.DeleteDatasetLabelsResponse: + workspace_id: str, + ) -> aiwork_space_20210204_models.DeleteWorkspaceResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.delete_dataset_labels_with_options(dataset_id, request, headers, runtime) + return self.delete_workspace_with_options(workspace_id, headers, runtime) - async def delete_dataset_labels_async( + async def delete_workspace_async( self, - dataset_id: str, - request: aiwork_space_20210204_models.DeleteDatasetLabelsRequest, - ) -> aiwork_space_20210204_models.DeleteDatasetLabelsResponse: + workspace_id: str, + ) -> aiwork_space_20210204_models.DeleteWorkspaceResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.delete_dataset_labels_with_options_async(dataset_id, request, headers, runtime) + return await self.delete_workspace_with_options_async(workspace_id, headers, runtime) - def delete_members_with_options( + def delete_workspace_resource_with_options( self, workspace_id: str, - request: aiwork_space_20210204_models.DeleteMembersRequest, + request: aiwork_space_20210204_models.DeleteWorkspaceResourceRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.DeleteMembersResponse: + ) -> aiwork_space_20210204_models.DeleteWorkspaceResourceResponse: UtilClient.validate_model(request) query = {} - if not UtilClient.is_unset(request.member_ids): - query['MemberIds'] = request.member_ids + if not UtilClient.is_unset(request.group_name): + query['GroupName'] = request.group_name + if not UtilClient.is_unset(request.labels): + query['Labels'] = request.labels + if not UtilClient.is_unset(request.option): + query['Option'] = request.option + if not UtilClient.is_unset(request.product_type): + query['ProductType'] = request.product_type + if not UtilClient.is_unset(request.resource_ids): + query['ResourceIds'] = request.resource_ids + if not UtilClient.is_unset(request.resource_type): + query['ResourceType'] = request.resource_type req = open_api_models.OpenApiRequest( headers=headers, query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='DeleteMembers', + action='DeleteWorkspaceResource', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/workspaces/{OpenApiUtilClient.get_encode_param(workspace_id)}/members', + pathname=f'/api/v1/workspaces/{OpenApiUtilClient.get_encode_param(workspace_id)}/resources', method='DELETE', auth_type='AK', style='ROA', @@ -2263,30 +3731,40 @@ def delete_members_with_options( body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.DeleteMembersResponse(), + aiwork_space_20210204_models.DeleteWorkspaceResourceResponse(), self.call_api(params, req, runtime) ) - async def delete_members_with_options_async( + async def delete_workspace_resource_with_options_async( self, workspace_id: str, - request: aiwork_space_20210204_models.DeleteMembersRequest, + request: aiwork_space_20210204_models.DeleteWorkspaceResourceRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.DeleteMembersResponse: + ) -> aiwork_space_20210204_models.DeleteWorkspaceResourceResponse: UtilClient.validate_model(request) query = {} - if not UtilClient.is_unset(request.member_ids): - query['MemberIds'] = request.member_ids + if not UtilClient.is_unset(request.group_name): + query['GroupName'] = request.group_name + if not UtilClient.is_unset(request.labels): + query['Labels'] = request.labels + if not UtilClient.is_unset(request.option): + query['Option'] = request.option + if not UtilClient.is_unset(request.product_type): + query['ProductType'] = request.product_type + if not UtilClient.is_unset(request.resource_ids): + query['ResourceIds'] = request.resource_ids + if not UtilClient.is_unset(request.resource_type): + query['ResourceType'] = request.resource_type req = open_api_models.OpenApiRequest( headers=headers, query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='DeleteMembers', + action='DeleteWorkspaceResource', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/workspaces/{OpenApiUtilClient.get_encode_param(workspace_id)}/members', + pathname=f'/api/v1/workspaces/{OpenApiUtilClient.get_encode_param(workspace_id)}/resources', method='DELETE', auth_type='AK', style='ROA', @@ -2294,588 +3772,552 @@ async def delete_members_with_options_async( body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.DeleteMembersResponse(), + aiwork_space_20210204_models.DeleteWorkspaceResourceResponse(), await self.call_api_async(params, req, runtime) ) - def delete_members( + def delete_workspace_resource( self, workspace_id: str, - request: aiwork_space_20210204_models.DeleteMembersRequest, - ) -> aiwork_space_20210204_models.DeleteMembersResponse: + request: aiwork_space_20210204_models.DeleteWorkspaceResourceRequest, + ) -> aiwork_space_20210204_models.DeleteWorkspaceResourceResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.delete_members_with_options(workspace_id, request, headers, runtime) + return self.delete_workspace_resource_with_options(workspace_id, request, headers, runtime) - async def delete_members_async( + async def delete_workspace_resource_async( self, workspace_id: str, - request: aiwork_space_20210204_models.DeleteMembersRequest, - ) -> aiwork_space_20210204_models.DeleteMembersResponse: + request: aiwork_space_20210204_models.DeleteWorkspaceResourceRequest, + ) -> aiwork_space_20210204_models.DeleteWorkspaceResourceResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.delete_members_with_options_async(workspace_id, request, headers, runtime) + return await self.delete_workspace_resource_with_options_async(workspace_id, request, headers, runtime) - def delete_model_with_options( + def describe_pricing_module_with_options( self, - model_id: str, + request: aiwork_space_20210204_models.DescribePricingModuleRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.DeleteModelResponse: + ) -> aiwork_space_20210204_models.DescribePricingModuleResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.product_code): + query['ProductCode'] = request.product_code + if not UtilClient.is_unset(request.product_type): + query['ProductType'] = request.product_type + if not UtilClient.is_unset(request.subscription_type): + query['SubscriptionType'] = request.subscription_type req = open_api_models.OpenApiRequest( - headers=headers + headers=headers, + query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='DeleteModel', + action='DescribePricingModule', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/models/{OpenApiUtilClient.get_encode_param(model_id)}', - method='DELETE', + pathname=f'/api/v1/proxy/describepricingmodule', + method='GET', auth_type='AK', style='ROA', req_body_type='json', body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.DeleteModelResponse(), + aiwork_space_20210204_models.DescribePricingModuleResponse(), self.call_api(params, req, runtime) ) - async def delete_model_with_options_async( + async def describe_pricing_module_with_options_async( self, - model_id: str, + request: aiwork_space_20210204_models.DescribePricingModuleRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.DeleteModelResponse: + ) -> aiwork_space_20210204_models.DescribePricingModuleResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.product_code): + query['ProductCode'] = request.product_code + if not UtilClient.is_unset(request.product_type): + query['ProductType'] = request.product_type + if not UtilClient.is_unset(request.subscription_type): + query['SubscriptionType'] = request.subscription_type req = open_api_models.OpenApiRequest( - headers=headers + headers=headers, + query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='DeleteModel', + action='DescribePricingModule', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/models/{OpenApiUtilClient.get_encode_param(model_id)}', - method='DELETE', + pathname=f'/api/v1/proxy/describepricingmodule', + method='GET', auth_type='AK', style='ROA', req_body_type='json', body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.DeleteModelResponse(), + aiwork_space_20210204_models.DescribePricingModuleResponse(), await self.call_api_async(params, req, runtime) ) - def delete_model( + def describe_pricing_module( self, - model_id: str, - ) -> aiwork_space_20210204_models.DeleteModelResponse: + request: aiwork_space_20210204_models.DescribePricingModuleRequest, + ) -> aiwork_space_20210204_models.DescribePricingModuleResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.delete_model_with_options(model_id, headers, runtime) + return self.describe_pricing_module_with_options(request, headers, runtime) - async def delete_model_async( + async def describe_pricing_module_async( self, - model_id: str, - ) -> aiwork_space_20210204_models.DeleteModelResponse: + request: aiwork_space_20210204_models.DescribePricingModuleRequest, + ) -> aiwork_space_20210204_models.DescribePricingModuleResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.delete_model_with_options_async(model_id, headers, runtime) + return await self.describe_pricing_module_with_options_async(request, headers, runtime) - def delete_model_domain_with_options( + def get_code_source_with_options( self, - model_domain_id: str, - request: aiwork_space_20210204_models.DeleteModelDomainRequest, + code_source_id: str, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.DeleteModelDomainResponse: - UtilClient.validate_model(request) - query = {} - if not UtilClient.is_unset(request.model_task_ids): - query['ModelTaskIds'] = request.model_task_ids + ) -> aiwork_space_20210204_models.GetCodeSourceResponse: req = open_api_models.OpenApiRequest( - headers=headers, - query=OpenApiUtilClient.query(query) + headers=headers ) params = open_api_models.Params( - action='DeleteModelDomain', + action='GetCodeSource', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/modeldomains/{OpenApiUtilClient.get_encode_param(model_domain_id)}', - method='DELETE', + pathname=f'/api/v1/codesources/{OpenApiUtilClient.get_encode_param(code_source_id)}', + method='GET', auth_type='AK', style='ROA', req_body_type='json', body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.DeleteModelDomainResponse(), + aiwork_space_20210204_models.GetCodeSourceResponse(), self.call_api(params, req, runtime) ) - async def delete_model_domain_with_options_async( + async def get_code_source_with_options_async( self, - model_domain_id: str, - request: aiwork_space_20210204_models.DeleteModelDomainRequest, + code_source_id: str, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.DeleteModelDomainResponse: - UtilClient.validate_model(request) - query = {} - if not UtilClient.is_unset(request.model_task_ids): - query['ModelTaskIds'] = request.model_task_ids + ) -> aiwork_space_20210204_models.GetCodeSourceResponse: req = open_api_models.OpenApiRequest( - headers=headers, - query=OpenApiUtilClient.query(query) + headers=headers ) params = open_api_models.Params( - action='DeleteModelDomain', + action='GetCodeSource', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/modeldomains/{OpenApiUtilClient.get_encode_param(model_domain_id)}', - method='DELETE', + pathname=f'/api/v1/codesources/{OpenApiUtilClient.get_encode_param(code_source_id)}', + method='GET', auth_type='AK', style='ROA', req_body_type='json', body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.DeleteModelDomainResponse(), + aiwork_space_20210204_models.GetCodeSourceResponse(), await self.call_api_async(params, req, runtime) ) - def delete_model_domain( + def get_code_source( self, - model_domain_id: str, - request: aiwork_space_20210204_models.DeleteModelDomainRequest, - ) -> aiwork_space_20210204_models.DeleteModelDomainResponse: + code_source_id: str, + ) -> aiwork_space_20210204_models.GetCodeSourceResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.delete_model_domain_with_options(model_domain_id, request, headers, runtime) + return self.get_code_source_with_options(code_source_id, headers, runtime) - async def delete_model_domain_async( + async def get_code_source_async( self, - model_domain_id: str, - request: aiwork_space_20210204_models.DeleteModelDomainRequest, - ) -> aiwork_space_20210204_models.DeleteModelDomainResponse: + code_source_id: str, + ) -> aiwork_space_20210204_models.GetCodeSourceResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.delete_model_domain_with_options_async(model_domain_id, request, headers, runtime) + return await self.get_code_source_with_options_async(code_source_id, headers, runtime) - def delete_model_labels_with_options( + def get_code_sources_statistics_with_options( self, - model_id: str, - request: aiwork_space_20210204_models.DeleteModelLabelsRequest, + request: aiwork_space_20210204_models.GetCodeSourcesStatisticsRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.DeleteModelLabelsResponse: + ) -> aiwork_space_20210204_models.GetCodeSourcesStatisticsResponse: UtilClient.validate_model(request) query = {} - if not UtilClient.is_unset(request.keys): - query['Keys'] = request.keys - if not UtilClient.is_unset(request.label_keys): - query['LabelKeys'] = request.label_keys + if not UtilClient.is_unset(request.workspace_id): + query['WorkspaceId'] = request.workspace_id req = open_api_models.OpenApiRequest( headers=headers, query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='DeleteModelLabels', + action='GetCodeSourcesStatistics', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/models/{OpenApiUtilClient.get_encode_param(model_id)}/labels', - method='DELETE', + pathname=f'/api/v1/statistics/codesources', + method='GET', auth_type='AK', style='ROA', req_body_type='json', body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.DeleteModelLabelsResponse(), + aiwork_space_20210204_models.GetCodeSourcesStatisticsResponse(), self.call_api(params, req, runtime) ) - async def delete_model_labels_with_options_async( + async def get_code_sources_statistics_with_options_async( self, - model_id: str, - request: aiwork_space_20210204_models.DeleteModelLabelsRequest, + request: aiwork_space_20210204_models.GetCodeSourcesStatisticsRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.DeleteModelLabelsResponse: + ) -> aiwork_space_20210204_models.GetCodeSourcesStatisticsResponse: UtilClient.validate_model(request) query = {} - if not UtilClient.is_unset(request.keys): - query['Keys'] = request.keys - if not UtilClient.is_unset(request.label_keys): - query['LabelKeys'] = request.label_keys + if not UtilClient.is_unset(request.workspace_id): + query['WorkspaceId'] = request.workspace_id req = open_api_models.OpenApiRequest( headers=headers, query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='DeleteModelLabels', + action='GetCodeSourcesStatistics', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/models/{OpenApiUtilClient.get_encode_param(model_id)}/labels', - method='DELETE', + pathname=f'/api/v1/statistics/codesources', + method='GET', auth_type='AK', style='ROA', req_body_type='json', body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.DeleteModelLabelsResponse(), + aiwork_space_20210204_models.GetCodeSourcesStatisticsResponse(), await self.call_api_async(params, req, runtime) ) - def delete_model_labels( + def get_code_sources_statistics( self, - model_id: str, - request: aiwork_space_20210204_models.DeleteModelLabelsRequest, - ) -> aiwork_space_20210204_models.DeleteModelLabelsResponse: + request: aiwork_space_20210204_models.GetCodeSourcesStatisticsRequest, + ) -> aiwork_space_20210204_models.GetCodeSourcesStatisticsResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.delete_model_labels_with_options(model_id, request, headers, runtime) + return self.get_code_sources_statistics_with_options(request, headers, runtime) - async def delete_model_labels_async( + async def get_code_sources_statistics_async( self, - model_id: str, - request: aiwork_space_20210204_models.DeleteModelLabelsRequest, - ) -> aiwork_space_20210204_models.DeleteModelLabelsResponse: + request: aiwork_space_20210204_models.GetCodeSourcesStatisticsRequest, + ) -> aiwork_space_20210204_models.GetCodeSourcesStatisticsResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.delete_model_labels_with_options_async(model_id, request, headers, runtime) + return await self.get_code_sources_statistics_with_options_async(request, headers, runtime) - def delete_model_version_with_options( + def get_collection_with_options( self, - model_id: str, - version_name: str, + collection_name: str, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.DeleteModelVersionResponse: + ) -> aiwork_space_20210204_models.GetCollectionResponse: req = open_api_models.OpenApiRequest( headers=headers ) params = open_api_models.Params( - action='DeleteModelVersion', + action='GetCollection', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/models/{OpenApiUtilClient.get_encode_param(model_id)}/versions/{OpenApiUtilClient.get_encode_param(version_name)}', - method='DELETE', + pathname=f'/api/v1/collections/{OpenApiUtilClient.get_encode_param(collection_name)}', + method='GET', auth_type='AK', style='ROA', req_body_type='json', body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.DeleteModelVersionResponse(), + aiwork_space_20210204_models.GetCollectionResponse(), self.call_api(params, req, runtime) ) - async def delete_model_version_with_options_async( + async def get_collection_with_options_async( self, - model_id: str, - version_name: str, + collection_name: str, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.DeleteModelVersionResponse: + ) -> aiwork_space_20210204_models.GetCollectionResponse: req = open_api_models.OpenApiRequest( headers=headers ) params = open_api_models.Params( - action='DeleteModelVersion', + action='GetCollection', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/models/{OpenApiUtilClient.get_encode_param(model_id)}/versions/{OpenApiUtilClient.get_encode_param(version_name)}', - method='DELETE', + pathname=f'/api/v1/collections/{OpenApiUtilClient.get_encode_param(collection_name)}', + method='GET', auth_type='AK', style='ROA', req_body_type='json', body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.DeleteModelVersionResponse(), + aiwork_space_20210204_models.GetCollectionResponse(), await self.call_api_async(params, req, runtime) ) - def delete_model_version( + def get_collection( self, - model_id: str, - version_name: str, - ) -> aiwork_space_20210204_models.DeleteModelVersionResponse: + collection_name: str, + ) -> aiwork_space_20210204_models.GetCollectionResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.delete_model_version_with_options(model_id, version_name, headers, runtime) + return self.get_collection_with_options(collection_name, headers, runtime) - async def delete_model_version_async( + async def get_collection_async( self, - model_id: str, - version_name: str, - ) -> aiwork_space_20210204_models.DeleteModelVersionResponse: + collection_name: str, + ) -> aiwork_space_20210204_models.GetCollectionResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.delete_model_version_with_options_async(model_id, version_name, headers, runtime) + return await self.get_collection_with_options_async(collection_name, headers, runtime) - def delete_model_version_labels_with_options( + def get_dataset_with_options( self, - model_id: str, - version_name: str, - request: aiwork_space_20210204_models.DeleteModelVersionLabelsRequest, + dataset_id: str, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.DeleteModelVersionLabelsResponse: - UtilClient.validate_model(request) - query = {} - if not UtilClient.is_unset(request.keys): - query['Keys'] = request.keys - if not UtilClient.is_unset(request.label_keys): - query['LabelKeys'] = request.label_keys + ) -> aiwork_space_20210204_models.GetDatasetResponse: req = open_api_models.OpenApiRequest( - headers=headers, - query=OpenApiUtilClient.query(query) + headers=headers ) params = open_api_models.Params( - action='DeleteModelVersionLabels', + action='GetDataset', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/models/{OpenApiUtilClient.get_encode_param(model_id)}/versions/{OpenApiUtilClient.get_encode_param(version_name)}/labels', - method='DELETE', + pathname=f'/api/v1/datasets/{OpenApiUtilClient.get_encode_param(dataset_id)}', + method='GET', auth_type='AK', style='ROA', req_body_type='json', body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.DeleteModelVersionLabelsResponse(), + aiwork_space_20210204_models.GetDatasetResponse(), self.call_api(params, req, runtime) ) - async def delete_model_version_labels_with_options_async( + async def get_dataset_with_options_async( self, - model_id: str, - version_name: str, - request: aiwork_space_20210204_models.DeleteModelVersionLabelsRequest, + dataset_id: str, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.DeleteModelVersionLabelsResponse: - UtilClient.validate_model(request) - query = {} - if not UtilClient.is_unset(request.keys): - query['Keys'] = request.keys - if not UtilClient.is_unset(request.label_keys): - query['LabelKeys'] = request.label_keys + ) -> aiwork_space_20210204_models.GetDatasetResponse: req = open_api_models.OpenApiRequest( - headers=headers, - query=OpenApiUtilClient.query(query) + headers=headers ) params = open_api_models.Params( - action='DeleteModelVersionLabels', + action='GetDataset', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/models/{OpenApiUtilClient.get_encode_param(model_id)}/versions/{OpenApiUtilClient.get_encode_param(version_name)}/labels', - method='DELETE', + pathname=f'/api/v1/datasets/{OpenApiUtilClient.get_encode_param(dataset_id)}', + method='GET', auth_type='AK', style='ROA', req_body_type='json', body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.DeleteModelVersionLabelsResponse(), + aiwork_space_20210204_models.GetDatasetResponse(), await self.call_api_async(params, req, runtime) ) - def delete_model_version_labels( + def get_dataset( self, - model_id: str, - version_name: str, - request: aiwork_space_20210204_models.DeleteModelVersionLabelsRequest, - ) -> aiwork_space_20210204_models.DeleteModelVersionLabelsResponse: + dataset_id: str, + ) -> aiwork_space_20210204_models.GetDatasetResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.delete_model_version_labels_with_options(model_id, version_name, request, headers, runtime) + return self.get_dataset_with_options(dataset_id, headers, runtime) - async def delete_model_version_labels_async( + async def get_dataset_async( self, - model_id: str, - version_name: str, - request: aiwork_space_20210204_models.DeleteModelVersionLabelsRequest, - ) -> aiwork_space_20210204_models.DeleteModelVersionLabelsResponse: + dataset_id: str, + ) -> aiwork_space_20210204_models.GetDatasetResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.delete_model_version_labels_with_options_async(model_id, version_name, request, headers, runtime) + return await self.get_dataset_with_options_async(dataset_id, headers, runtime) - def delete_workspace_with_options( + def get_datasets_statistics_with_options( self, - workspace_id: str, + request: aiwork_space_20210204_models.GetDatasetsStatisticsRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.DeleteWorkspaceResponse: + ) -> aiwork_space_20210204_models.GetDatasetsStatisticsResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.workspace_id): + query['WorkspaceId'] = request.workspace_id req = open_api_models.OpenApiRequest( - headers=headers + headers=headers, + query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='DeleteWorkspace', + action='GetDatasetsStatistics', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/workspaces/{OpenApiUtilClient.get_encode_param(workspace_id)}', - method='DELETE', + pathname=f'/api/v1/statistics/datasets', + method='GET', auth_type='AK', style='ROA', req_body_type='json', body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.DeleteWorkspaceResponse(), + aiwork_space_20210204_models.GetDatasetsStatisticsResponse(), self.call_api(params, req, runtime) ) - async def delete_workspace_with_options_async( + async def get_datasets_statistics_with_options_async( self, - workspace_id: str, + request: aiwork_space_20210204_models.GetDatasetsStatisticsRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.DeleteWorkspaceResponse: + ) -> aiwork_space_20210204_models.GetDatasetsStatisticsResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.workspace_id): + query['WorkspaceId'] = request.workspace_id req = open_api_models.OpenApiRequest( - headers=headers + headers=headers, + query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='DeleteWorkspace', + action='GetDatasetsStatistics', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/workspaces/{OpenApiUtilClient.get_encode_param(workspace_id)}', - method='DELETE', + pathname=f'/api/v1/statistics/datasets', + method='GET', auth_type='AK', style='ROA', req_body_type='json', body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.DeleteWorkspaceResponse(), + aiwork_space_20210204_models.GetDatasetsStatisticsResponse(), await self.call_api_async(params, req, runtime) ) - def delete_workspace( + def get_datasets_statistics( self, - workspace_id: str, - ) -> aiwork_space_20210204_models.DeleteWorkspaceResponse: + request: aiwork_space_20210204_models.GetDatasetsStatisticsRequest, + ) -> aiwork_space_20210204_models.GetDatasetsStatisticsResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.delete_workspace_with_options(workspace_id, headers, runtime) + return self.get_datasets_statistics_with_options(request, headers, runtime) - async def delete_workspace_async( + async def get_datasets_statistics_async( self, - workspace_id: str, - ) -> aiwork_space_20210204_models.DeleteWorkspaceResponse: + request: aiwork_space_20210204_models.GetDatasetsStatisticsRequest, + ) -> aiwork_space_20210204_models.GetDatasetsStatisticsResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.delete_workspace_with_options_async(workspace_id, headers, runtime) + return await self.get_datasets_statistics_with_options_async(request, headers, runtime) - def delete_workspace_resource_with_options( + def get_default_workspace_with_options( self, - workspace_id: str, - request: aiwork_space_20210204_models.DeleteWorkspaceResourceRequest, + request: aiwork_space_20210204_models.GetDefaultWorkspaceRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.DeleteWorkspaceResourceResponse: + ) -> aiwork_space_20210204_models.GetDefaultWorkspaceResponse: UtilClient.validate_model(request) query = {} - if not UtilClient.is_unset(request.group_name): - query['GroupName'] = request.group_name - if not UtilClient.is_unset(request.option): - query['Option'] = request.option - if not UtilClient.is_unset(request.product_type): - query['ProductType'] = request.product_type - if not UtilClient.is_unset(request.resource_type): - query['ResourceType'] = request.resource_type + if not UtilClient.is_unset(request.verbose): + query['Verbose'] = request.verbose req = open_api_models.OpenApiRequest( headers=headers, query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='DeleteWorkspaceResource', + action='GetDefaultWorkspace', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/workspaces/{OpenApiUtilClient.get_encode_param(workspace_id)}/resources', - method='DELETE', + pathname=f'/api/v1/defaultWorkspaces', + method='GET', auth_type='AK', style='ROA', req_body_type='json', body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.DeleteWorkspaceResourceResponse(), + aiwork_space_20210204_models.GetDefaultWorkspaceResponse(), self.call_api(params, req, runtime) ) - async def delete_workspace_resource_with_options_async( + async def get_default_workspace_with_options_async( self, - workspace_id: str, - request: aiwork_space_20210204_models.DeleteWorkspaceResourceRequest, + request: aiwork_space_20210204_models.GetDefaultWorkspaceRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.DeleteWorkspaceResourceResponse: + ) -> aiwork_space_20210204_models.GetDefaultWorkspaceResponse: UtilClient.validate_model(request) query = {} - if not UtilClient.is_unset(request.group_name): - query['GroupName'] = request.group_name - if not UtilClient.is_unset(request.option): - query['Option'] = request.option - if not UtilClient.is_unset(request.product_type): - query['ProductType'] = request.product_type - if not UtilClient.is_unset(request.resource_type): - query['ResourceType'] = request.resource_type + if not UtilClient.is_unset(request.verbose): + query['Verbose'] = request.verbose req = open_api_models.OpenApiRequest( headers=headers, query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='DeleteWorkspaceResource', + action='GetDefaultWorkspace', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/workspaces/{OpenApiUtilClient.get_encode_param(workspace_id)}/resources', - method='DELETE', + pathname=f'/api/v1/defaultWorkspaces', + method='GET', auth_type='AK', style='ROA', req_body_type='json', body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.DeleteWorkspaceResourceResponse(), + aiwork_space_20210204_models.GetDefaultWorkspaceResponse(), await self.call_api_async(params, req, runtime) ) - def delete_workspace_resource( + def get_default_workspace( self, - workspace_id: str, - request: aiwork_space_20210204_models.DeleteWorkspaceResourceRequest, - ) -> aiwork_space_20210204_models.DeleteWorkspaceResourceResponse: + request: aiwork_space_20210204_models.GetDefaultWorkspaceRequest, + ) -> aiwork_space_20210204_models.GetDefaultWorkspaceResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.delete_workspace_resource_with_options(workspace_id, request, headers, runtime) + return self.get_default_workspace_with_options(request, headers, runtime) - async def delete_workspace_resource_async( + async def get_default_workspace_async( self, - workspace_id: str, - request: aiwork_space_20210204_models.DeleteWorkspaceResourceRequest, - ) -> aiwork_space_20210204_models.DeleteWorkspaceResourceResponse: + request: aiwork_space_20210204_models.GetDefaultWorkspaceRequest, + ) -> aiwork_space_20210204_models.GetDefaultWorkspaceResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.delete_workspace_resource_with_options_async(workspace_id, request, headers, runtime) + return await self.get_default_workspace_with_options_async(request, headers, runtime) - def get_code_source_with_options( + def get_experiment_with_options( self, - code_source_id: str, + experiment_id: str, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.GetCodeSourceResponse: + ) -> aiwork_space_20210204_models.GetExperimentResponse: req = open_api_models.OpenApiRequest( headers=headers ) params = open_api_models.Params( - action='GetCodeSource', + action='GetExperiment', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/codesources/{OpenApiUtilClient.get_encode_param(code_source_id)}', + pathname=f'/api/v1/experiments/{OpenApiUtilClient.get_encode_param(experiment_id)}', method='GET', auth_type='AK', style='ROA', @@ -2883,24 +4325,24 @@ def get_code_source_with_options( body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.GetCodeSourceResponse(), + aiwork_space_20210204_models.GetExperimentResponse(), self.call_api(params, req, runtime) ) - async def get_code_source_with_options_async( + async def get_experiment_with_options_async( self, - code_source_id: str, + experiment_id: str, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.GetCodeSourceResponse: + ) -> aiwork_space_20210204_models.GetExperimentResponse: req = open_api_models.OpenApiRequest( headers=headers ) params = open_api_models.Params( - action='GetCodeSource', + action='GetExperiment', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/codesources/{OpenApiUtilClient.get_encode_param(code_source_id)}', + pathname=f'/api/v1/experiments/{OpenApiUtilClient.get_encode_param(experiment_id)}', method='GET', auth_type='AK', style='ROA', @@ -2908,45 +4350,46 @@ async def get_code_source_with_options_async( body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.GetCodeSourceResponse(), + aiwork_space_20210204_models.GetExperimentResponse(), await self.call_api_async(params, req, runtime) ) - def get_code_source( + def get_experiment( self, - code_source_id: str, - ) -> aiwork_space_20210204_models.GetCodeSourceResponse: + experiment_id: str, + ) -> aiwork_space_20210204_models.GetExperimentResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.get_code_source_with_options(code_source_id, headers, runtime) + return self.get_experiment_with_options(experiment_id, headers, runtime) - async def get_code_source_async( + async def get_experiment_async( self, - code_source_id: str, - ) -> aiwork_space_20210204_models.GetCodeSourceResponse: + experiment_id: str, + ) -> aiwork_space_20210204_models.GetExperimentResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.get_code_source_with_options_async(code_source_id, headers, runtime) + return await self.get_experiment_with_options_async(experiment_id, headers, runtime) - def get_code_sources_statistics_with_options( + def get_image_with_options( self, - request: aiwork_space_20210204_models.GetCodeSourcesStatisticsRequest, + image_id: str, + request: aiwork_space_20210204_models.GetImageRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.GetCodeSourcesStatisticsResponse: + ) -> aiwork_space_20210204_models.GetImageResponse: UtilClient.validate_model(request) query = {} - if not UtilClient.is_unset(request.workspace_id): - query['WorkspaceId'] = request.workspace_id + if not UtilClient.is_unset(request.verbose): + query['Verbose'] = request.verbose req = open_api_models.OpenApiRequest( headers=headers, query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='GetCodeSourcesStatistics', + action='GetImage', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/statistics/codesources', + pathname=f'/api/v1/images/{OpenApiUtilClient.get_encode_param(image_id)}', method='GET', auth_type='AK', style='ROA', @@ -2954,29 +4397,30 @@ def get_code_sources_statistics_with_options( body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.GetCodeSourcesStatisticsResponse(), + aiwork_space_20210204_models.GetImageResponse(), self.call_api(params, req, runtime) ) - async def get_code_sources_statistics_with_options_async( + async def get_image_with_options_async( self, - request: aiwork_space_20210204_models.GetCodeSourcesStatisticsRequest, + image_id: str, + request: aiwork_space_20210204_models.GetImageRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.GetCodeSourcesStatisticsResponse: + ) -> aiwork_space_20210204_models.GetImageResponse: UtilClient.validate_model(request) query = {} - if not UtilClient.is_unset(request.workspace_id): - query['WorkspaceId'] = request.workspace_id + if not UtilClient.is_unset(request.verbose): + query['Verbose'] = request.verbose req = open_api_models.OpenApiRequest( headers=headers, query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='GetCodeSourcesStatistics', + action='GetImage', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/statistics/codesources', + pathname=f'/api/v1/images/{OpenApiUtilClient.get_encode_param(image_id)}', method='GET', auth_type='AK', style='ROA', @@ -2984,40 +4428,47 @@ async def get_code_sources_statistics_with_options_async( body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.GetCodeSourcesStatisticsResponse(), + aiwork_space_20210204_models.GetImageResponse(), await self.call_api_async(params, req, runtime) ) - def get_code_sources_statistics( + def get_image( self, - request: aiwork_space_20210204_models.GetCodeSourcesStatisticsRequest, - ) -> aiwork_space_20210204_models.GetCodeSourcesStatisticsResponse: + image_id: str, + request: aiwork_space_20210204_models.GetImageRequest, + ) -> aiwork_space_20210204_models.GetImageResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.get_code_sources_statistics_with_options(request, headers, runtime) + return self.get_image_with_options(image_id, request, headers, runtime) - async def get_code_sources_statistics_async( + async def get_image_async( self, - request: aiwork_space_20210204_models.GetCodeSourcesStatisticsRequest, - ) -> aiwork_space_20210204_models.GetCodeSourcesStatisticsResponse: + image_id: str, + request: aiwork_space_20210204_models.GetImageRequest, + ) -> aiwork_space_20210204_models.GetImageResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.get_code_sources_statistics_with_options_async(request, headers, runtime) + return await self.get_image_with_options_async(image_id, request, headers, runtime) - def get_dataset_with_options( + def get_images_statistics_with_options( self, - dataset_id: str, + request: aiwork_space_20210204_models.GetImagesStatisticsRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.GetDatasetResponse: + ) -> aiwork_space_20210204_models.GetImagesStatisticsResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.workspace_id): + query['WorkspaceId'] = request.workspace_id req = open_api_models.OpenApiRequest( - headers=headers + headers=headers, + query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='GetDataset', + action='GetImagesStatistics', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/datasets/{OpenApiUtilClient.get_encode_param(dataset_id)}', + pathname=f'/api/v1/statistics/images', method='GET', auth_type='AK', style='ROA', @@ -3025,24 +4476,29 @@ def get_dataset_with_options( body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.GetDatasetResponse(), + aiwork_space_20210204_models.GetImagesStatisticsResponse(), self.call_api(params, req, runtime) ) - async def get_dataset_with_options_async( + async def get_images_statistics_with_options_async( self, - dataset_id: str, + request: aiwork_space_20210204_models.GetImagesStatisticsRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.GetDatasetResponse: + ) -> aiwork_space_20210204_models.GetImagesStatisticsResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.workspace_id): + query['WorkspaceId'] = request.workspace_id req = open_api_models.OpenApiRequest( - headers=headers + headers=headers, + query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='GetDataset', + action='GetImagesStatistics', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/datasets/{OpenApiUtilClient.get_encode_param(dataset_id)}', + pathname=f'/api/v1/statistics/images', method='GET', auth_type='AK', style='ROA', @@ -3050,45 +4506,48 @@ async def get_dataset_with_options_async( body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.GetDatasetResponse(), + aiwork_space_20210204_models.GetImagesStatisticsResponse(), await self.call_api_async(params, req, runtime) ) - def get_dataset( + def get_images_statistics( self, - dataset_id: str, - ) -> aiwork_space_20210204_models.GetDatasetResponse: + request: aiwork_space_20210204_models.GetImagesStatisticsRequest, + ) -> aiwork_space_20210204_models.GetImagesStatisticsResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.get_dataset_with_options(dataset_id, headers, runtime) + return self.get_images_statistics_with_options(request, headers, runtime) - async def get_dataset_async( + async def get_images_statistics_async( self, - dataset_id: str, - ) -> aiwork_space_20210204_models.GetDatasetResponse: + request: aiwork_space_20210204_models.GetImagesStatisticsRequest, + ) -> aiwork_space_20210204_models.GetImagesStatisticsResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.get_dataset_with_options_async(dataset_id, headers, runtime) + return await self.get_images_statistics_with_options_async(request, headers, runtime) - def get_datasets_statistics_with_options( + def get_member_with_options( self, - request: aiwork_space_20210204_models.GetDatasetsStatisticsRequest, + workspace_id: str, + request: aiwork_space_20210204_models.GetMemberRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.GetDatasetsStatisticsResponse: + ) -> aiwork_space_20210204_models.GetMemberResponse: UtilClient.validate_model(request) query = {} - if not UtilClient.is_unset(request.workspace_id): - query['WorkspaceId'] = request.workspace_id + if not UtilClient.is_unset(request.member_id): + query['MemberId'] = request.member_id + if not UtilClient.is_unset(request.user_id): + query['UserId'] = request.user_id req = open_api_models.OpenApiRequest( headers=headers, query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='GetDatasetsStatistics', + action='GetMember', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/statistics/datasets', + pathname=f'/api/v1/workspaces/{OpenApiUtilClient.get_encode_param(workspace_id)}/member', method='GET', auth_type='AK', style='ROA', @@ -3096,29 +4555,32 @@ def get_datasets_statistics_with_options( body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.GetDatasetsStatisticsResponse(), + aiwork_space_20210204_models.GetMemberResponse(), self.call_api(params, req, runtime) ) - async def get_datasets_statistics_with_options_async( + async def get_member_with_options_async( self, - request: aiwork_space_20210204_models.GetDatasetsStatisticsRequest, + workspace_id: str, + request: aiwork_space_20210204_models.GetMemberRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.GetDatasetsStatisticsResponse: + ) -> aiwork_space_20210204_models.GetMemberResponse: UtilClient.validate_model(request) query = {} - if not UtilClient.is_unset(request.workspace_id): - query['WorkspaceId'] = request.workspace_id + if not UtilClient.is_unset(request.member_id): + query['MemberId'] = request.member_id + if not UtilClient.is_unset(request.user_id): + query['UserId'] = request.user_id req = open_api_models.OpenApiRequest( headers=headers, query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='GetDatasetsStatistics', + action='GetMember', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/statistics/datasets', + pathname=f'/api/v1/workspaces/{OpenApiUtilClient.get_encode_param(workspace_id)}/member', method='GET', auth_type='AK', style='ROA', @@ -3126,45 +4588,42 @@ async def get_datasets_statistics_with_options_async( body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.GetDatasetsStatisticsResponse(), + aiwork_space_20210204_models.GetMemberResponse(), await self.call_api_async(params, req, runtime) ) - def get_datasets_statistics( + def get_member( self, - request: aiwork_space_20210204_models.GetDatasetsStatisticsRequest, - ) -> aiwork_space_20210204_models.GetDatasetsStatisticsResponse: + workspace_id: str, + request: aiwork_space_20210204_models.GetMemberRequest, + ) -> aiwork_space_20210204_models.GetMemberResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.get_datasets_statistics_with_options(request, headers, runtime) + return self.get_member_with_options(workspace_id, request, headers, runtime) - async def get_datasets_statistics_async( + async def get_member_async( self, - request: aiwork_space_20210204_models.GetDatasetsStatisticsRequest, - ) -> aiwork_space_20210204_models.GetDatasetsStatisticsResponse: + workspace_id: str, + request: aiwork_space_20210204_models.GetMemberRequest, + ) -> aiwork_space_20210204_models.GetMemberResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.get_datasets_statistics_with_options_async(request, headers, runtime) + return await self.get_member_with_options_async(workspace_id, request, headers, runtime) - def get_default_workspace_with_options( + def get_model_with_options( self, - request: aiwork_space_20210204_models.GetDefaultWorkspaceRequest, + model_id: str, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.GetDefaultWorkspaceResponse: - UtilClient.validate_model(request) - query = {} - if not UtilClient.is_unset(request.verbose): - query['Verbose'] = request.verbose + ) -> aiwork_space_20210204_models.GetModelResponse: req = open_api_models.OpenApiRequest( - headers=headers, - query=OpenApiUtilClient.query(query) + headers=headers ) params = open_api_models.Params( - action='GetDefaultWorkspace', + action='GetModel', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/defaultWorkspaces', + pathname=f'/api/v1/models/{OpenApiUtilClient.get_encode_param(model_id)}', method='GET', auth_type='AK', style='ROA', @@ -3172,29 +4631,24 @@ def get_default_workspace_with_options( body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.GetDefaultWorkspaceResponse(), + aiwork_space_20210204_models.GetModelResponse(), self.call_api(params, req, runtime) ) - async def get_default_workspace_with_options_async( + async def get_model_with_options_async( self, - request: aiwork_space_20210204_models.GetDefaultWorkspaceRequest, + model_id: str, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.GetDefaultWorkspaceResponse: - UtilClient.validate_model(request) - query = {} - if not UtilClient.is_unset(request.verbose): - query['Verbose'] = request.verbose + ) -> aiwork_space_20210204_models.GetModelResponse: req = open_api_models.OpenApiRequest( - headers=headers, - query=OpenApiUtilClient.query(query) + headers=headers ) params = open_api_models.Params( - action='GetDefaultWorkspace', + action='GetModel', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/defaultWorkspaces', + pathname=f'/api/v1/models/{OpenApiUtilClient.get_encode_param(model_id)}', method='GET', auth_type='AK', style='ROA', @@ -3202,46 +4656,41 @@ async def get_default_workspace_with_options_async( body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.GetDefaultWorkspaceResponse(), + aiwork_space_20210204_models.GetModelResponse(), await self.call_api_async(params, req, runtime) ) - def get_default_workspace( + def get_model( self, - request: aiwork_space_20210204_models.GetDefaultWorkspaceRequest, - ) -> aiwork_space_20210204_models.GetDefaultWorkspaceResponse: + model_id: str, + ) -> aiwork_space_20210204_models.GetModelResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.get_default_workspace_with_options(request, headers, runtime) + return self.get_model_with_options(model_id, headers, runtime) - async def get_default_workspace_async( + async def get_model_async( self, - request: aiwork_space_20210204_models.GetDefaultWorkspaceRequest, - ) -> aiwork_space_20210204_models.GetDefaultWorkspaceResponse: + model_id: str, + ) -> aiwork_space_20210204_models.GetModelResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.get_default_workspace_with_options_async(request, headers, runtime) + return await self.get_model_with_options_async(model_id, headers, runtime) - def get_image_with_options( + def get_model_version_with_options( self, - image_id: str, - request: aiwork_space_20210204_models.GetImageRequest, + model_id: str, + version_name: str, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.GetImageResponse: - UtilClient.validate_model(request) - query = {} - if not UtilClient.is_unset(request.verbose): - query['Verbose'] = request.verbose + ) -> aiwork_space_20210204_models.GetModelVersionResponse: req = open_api_models.OpenApiRequest( - headers=headers, - query=OpenApiUtilClient.query(query) + headers=headers ) params = open_api_models.Params( - action='GetImage', + action='GetModelVersion', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/images/{OpenApiUtilClient.get_encode_param(image_id)}', + pathname=f'/api/v1/models/{OpenApiUtilClient.get_encode_param(model_id)}/versions/{OpenApiUtilClient.get_encode_param(version_name)}', method='GET', auth_type='AK', style='ROA', @@ -3249,30 +4698,25 @@ def get_image_with_options( body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.GetImageResponse(), + aiwork_space_20210204_models.GetModelVersionResponse(), self.call_api(params, req, runtime) ) - async def get_image_with_options_async( + async def get_model_version_with_options_async( self, - image_id: str, - request: aiwork_space_20210204_models.GetImageRequest, + model_id: str, + version_name: str, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.GetImageResponse: - UtilClient.validate_model(request) - query = {} - if not UtilClient.is_unset(request.verbose): - query['Verbose'] = request.verbose + ) -> aiwork_space_20210204_models.GetModelVersionResponse: req = open_api_models.OpenApiRequest( - headers=headers, - query=OpenApiUtilClient.query(query) + headers=headers ) params = open_api_models.Params( - action='GetImage', + action='GetModelVersion', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/images/{OpenApiUtilClient.get_encode_param(image_id)}', + pathname=f'/api/v1/models/{OpenApiUtilClient.get_encode_param(model_id)}/versions/{OpenApiUtilClient.get_encode_param(version_name)}', method='GET', auth_type='AK', style='ROA', @@ -3280,124 +4724,139 @@ async def get_image_with_options_async( body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.GetImageResponse(), + aiwork_space_20210204_models.GetModelVersionResponse(), await self.call_api_async(params, req, runtime) ) - def get_image( + def get_model_version( self, - image_id: str, - request: aiwork_space_20210204_models.GetImageRequest, - ) -> aiwork_space_20210204_models.GetImageResponse: + model_id: str, + version_name: str, + ) -> aiwork_space_20210204_models.GetModelVersionResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.get_image_with_options(image_id, request, headers, runtime) + return self.get_model_version_with_options(model_id, version_name, headers, runtime) - async def get_image_async( + async def get_model_version_async( self, - image_id: str, - request: aiwork_space_20210204_models.GetImageRequest, - ) -> aiwork_space_20210204_models.GetImageResponse: + model_id: str, + version_name: str, + ) -> aiwork_space_20210204_models.GetModelVersionResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.get_image_with_options_async(image_id, request, headers, runtime) + return await self.get_model_version_with_options_async(model_id, version_name, headers, runtime) - def get_images_statistics_with_options( + def get_pay_as_you_go_price_with_options( self, - request: aiwork_space_20210204_models.GetImagesStatisticsRequest, + request: aiwork_space_20210204_models.GetPayAsYouGoPriceRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.GetImagesStatisticsResponse: + ) -> aiwork_space_20210204_models.GetPayAsYouGoPriceResponse: UtilClient.validate_model(request) - query = {} - if not UtilClient.is_unset(request.workspace_id): - query['WorkspaceId'] = request.workspace_id + body = {} + if not UtilClient.is_unset(request.module_list): + body['ModuleList'] = request.module_list + if not UtilClient.is_unset(request.product_code): + body['ProductCode'] = request.product_code + if not UtilClient.is_unset(request.product_type): + body['ProductType'] = request.product_type + if not UtilClient.is_unset(request.subscription_type): + body['SubscriptionType'] = request.subscription_type req = open_api_models.OpenApiRequest( headers=headers, - query=OpenApiUtilClient.query(query) + body=OpenApiUtilClient.parse_to_map(body) ) params = open_api_models.Params( - action='GetImagesStatistics', + action='GetPayAsYouGoPrice', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/statistics/images', - method='GET', + pathname=f'/api/v1/proxy/getpayasyougoprice', + method='POST', auth_type='AK', style='ROA', req_body_type='json', body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.GetImagesStatisticsResponse(), + aiwork_space_20210204_models.GetPayAsYouGoPriceResponse(), self.call_api(params, req, runtime) ) - async def get_images_statistics_with_options_async( + async def get_pay_as_you_go_price_with_options_async( self, - request: aiwork_space_20210204_models.GetImagesStatisticsRequest, + request: aiwork_space_20210204_models.GetPayAsYouGoPriceRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.GetImagesStatisticsResponse: + ) -> aiwork_space_20210204_models.GetPayAsYouGoPriceResponse: UtilClient.validate_model(request) - query = {} - if not UtilClient.is_unset(request.workspace_id): - query['WorkspaceId'] = request.workspace_id + body = {} + if not UtilClient.is_unset(request.module_list): + body['ModuleList'] = request.module_list + if not UtilClient.is_unset(request.product_code): + body['ProductCode'] = request.product_code + if not UtilClient.is_unset(request.product_type): + body['ProductType'] = request.product_type + if not UtilClient.is_unset(request.subscription_type): + body['SubscriptionType'] = request.subscription_type req = open_api_models.OpenApiRequest( headers=headers, - query=OpenApiUtilClient.query(query) + body=OpenApiUtilClient.parse_to_map(body) ) params = open_api_models.Params( - action='GetImagesStatistics', + action='GetPayAsYouGoPrice', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/statistics/images', - method='GET', + pathname=f'/api/v1/proxy/getpayasyougoprice', + method='POST', auth_type='AK', style='ROA', req_body_type='json', body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.GetImagesStatisticsResponse(), + aiwork_space_20210204_models.GetPayAsYouGoPriceResponse(), await self.call_api_async(params, req, runtime) ) - def get_images_statistics( + def get_pay_as_you_go_price( self, - request: aiwork_space_20210204_models.GetImagesStatisticsRequest, - ) -> aiwork_space_20210204_models.GetImagesStatisticsResponse: + request: aiwork_space_20210204_models.GetPayAsYouGoPriceRequest, + ) -> aiwork_space_20210204_models.GetPayAsYouGoPriceResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.get_images_statistics_with_options(request, headers, runtime) + return self.get_pay_as_you_go_price_with_options(request, headers, runtime) - async def get_images_statistics_async( + async def get_pay_as_you_go_price_async( self, - request: aiwork_space_20210204_models.GetImagesStatisticsRequest, - ) -> aiwork_space_20210204_models.GetImagesStatisticsResponse: + request: aiwork_space_20210204_models.GetPayAsYouGoPriceRequest, + ) -> aiwork_space_20210204_models.GetPayAsYouGoPriceResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.get_images_statistics_with_options_async(request, headers, runtime) + return await self.get_pay_as_you_go_price_with_options_async(request, headers, runtime) - def get_member_with_options( + def get_permission_with_options( self, workspace_id: str, - request: aiwork_space_20210204_models.GetMemberRequest, + permission_code: str, + request: aiwork_space_20210204_models.GetPermissionRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.GetMemberResponse: + ) -> aiwork_space_20210204_models.GetPermissionResponse: UtilClient.validate_model(request) query = {} - if not UtilClient.is_unset(request.user_id): - query['UserId'] = request.user_id + if not UtilClient.is_unset(request.accessibility): + query['Accessibility'] = request.accessibility + if not UtilClient.is_unset(request.creator): + query['Creator'] = request.creator req = open_api_models.OpenApiRequest( headers=headers, query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='GetMember', + action='GetPermission', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/workspaces/{OpenApiUtilClient.get_encode_param(workspace_id)}/member', + pathname=f'/api/v1/workspaces/{OpenApiUtilClient.get_encode_param(workspace_id)}/permissions/{OpenApiUtilClient.get_encode_param(permission_code)}', method='GET', auth_type='AK', style='ROA', @@ -3405,30 +4864,33 @@ def get_member_with_options( body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.GetMemberResponse(), + aiwork_space_20210204_models.GetPermissionResponse(), self.call_api(params, req, runtime) ) - async def get_member_with_options_async( + async def get_permission_with_options_async( self, workspace_id: str, - request: aiwork_space_20210204_models.GetMemberRequest, + permission_code: str, + request: aiwork_space_20210204_models.GetPermissionRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.GetMemberResponse: + ) -> aiwork_space_20210204_models.GetPermissionResponse: UtilClient.validate_model(request) query = {} - if not UtilClient.is_unset(request.user_id): - query['UserId'] = request.user_id + if not UtilClient.is_unset(request.accessibility): + query['Accessibility'] = request.accessibility + if not UtilClient.is_unset(request.creator): + query['Creator'] = request.creator req = open_api_models.OpenApiRequest( headers=headers, query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='GetMember', + action='GetPermission', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/workspaces/{OpenApiUtilClient.get_encode_param(workspace_id)}/member', + pathname=f'/api/v1/workspaces/{OpenApiUtilClient.get_encode_param(workspace_id)}/permissions/{OpenApiUtilClient.get_encode_param(permission_code)}', method='GET', auth_type='AK', style='ROA', @@ -3436,42 +4898,51 @@ async def get_member_with_options_async( body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.GetMemberResponse(), + aiwork_space_20210204_models.GetPermissionResponse(), await self.call_api_async(params, req, runtime) ) - def get_member( + def get_permission( self, workspace_id: str, - request: aiwork_space_20210204_models.GetMemberRequest, - ) -> aiwork_space_20210204_models.GetMemberResponse: + permission_code: str, + request: aiwork_space_20210204_models.GetPermissionRequest, + ) -> aiwork_space_20210204_models.GetPermissionResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.get_member_with_options(workspace_id, request, headers, runtime) + return self.get_permission_with_options(workspace_id, permission_code, request, headers, runtime) - async def get_member_async( + async def get_permission_async( self, workspace_id: str, - request: aiwork_space_20210204_models.GetMemberRequest, - ) -> aiwork_space_20210204_models.GetMemberResponse: + permission_code: str, + request: aiwork_space_20210204_models.GetPermissionRequest, + ) -> aiwork_space_20210204_models.GetPermissionResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.get_member_with_options_async(workspace_id, request, headers, runtime) + return await self.get_permission_with_options_async(workspace_id, permission_code, request, headers, runtime) - def get_model_with_options( + def get_resource_with_options( self, - model_id: str, + resource_id: str, + workspace_id: str, + request: aiwork_space_20210204_models.GetResourceRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.GetModelResponse: + ) -> aiwork_space_20210204_models.GetResourceResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.resource_type): + query['ResourceType'] = request.resource_type req = open_api_models.OpenApiRequest( - headers=headers + headers=headers, + query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='GetModel', + action='GetResource', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/models/{OpenApiUtilClient.get_encode_param(model_id)}', + pathname=f'/api/v1/workspaces/{OpenApiUtilClient.get_encode_param(workspace_id)}/resources/{OpenApiUtilClient.get_encode_param(resource_id)}', method='GET', auth_type='AK', style='ROA', @@ -3479,24 +4950,31 @@ def get_model_with_options( body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.GetModelResponse(), + aiwork_space_20210204_models.GetResourceResponse(), self.call_api(params, req, runtime) ) - async def get_model_with_options_async( + async def get_resource_with_options_async( self, - model_id: str, + resource_id: str, + workspace_id: str, + request: aiwork_space_20210204_models.GetResourceRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.GetModelResponse: + ) -> aiwork_space_20210204_models.GetResourceResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.resource_type): + query['ResourceType'] = request.resource_type req = open_api_models.OpenApiRequest( - headers=headers + headers=headers, + query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='GetModel', + action='GetResource', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/models/{OpenApiUtilClient.get_encode_param(model_id)}', + pathname=f'/api/v1/workspaces/{OpenApiUtilClient.get_encode_param(workspace_id)}/resources/{OpenApiUtilClient.get_encode_param(resource_id)}', method='GET', auth_type='AK', style='ROA', @@ -3504,41 +4982,49 @@ async def get_model_with_options_async( body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.GetModelResponse(), + aiwork_space_20210204_models.GetResourceResponse(), await self.call_api_async(params, req, runtime) ) - def get_model( + def get_resource( self, - model_id: str, - ) -> aiwork_space_20210204_models.GetModelResponse: + resource_id: str, + workspace_id: str, + request: aiwork_space_20210204_models.GetResourceRequest, + ) -> aiwork_space_20210204_models.GetResourceResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.get_model_with_options(model_id, headers, runtime) + return self.get_resource_with_options(resource_id, workspace_id, request, headers, runtime) - async def get_model_async( + async def get_resource_async( self, - model_id: str, - ) -> aiwork_space_20210204_models.GetModelResponse: + resource_id: str, + workspace_id: str, + request: aiwork_space_20210204_models.GetResourceRequest, + ) -> aiwork_space_20210204_models.GetResourceResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.get_model_with_options_async(model_id, headers, runtime) + return await self.get_resource_with_options_async(resource_id, workspace_id, request, headers, runtime) - def get_model_version_with_options( + def get_role_statistics_with_options( self, - model_id: str, - version_name: str, + request: aiwork_space_20210204_models.GetRoleStatisticsRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.GetModelVersionResponse: + ) -> aiwork_space_20210204_models.GetRoleStatisticsResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.workspace_id): + query['WorkspaceId'] = request.workspace_id req = open_api_models.OpenApiRequest( - headers=headers + headers=headers, + query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='GetModelVersion', + action='GetRoleStatistics', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/models/{OpenApiUtilClient.get_encode_param(model_id)}/versions/{OpenApiUtilClient.get_encode_param(version_name)}', + pathname=f'/api/v1/statistics/roles', method='GET', auth_type='AK', style='ROA', @@ -3546,25 +5032,29 @@ def get_model_version_with_options( body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.GetModelVersionResponse(), + aiwork_space_20210204_models.GetRoleStatisticsResponse(), self.call_api(params, req, runtime) ) - async def get_model_version_with_options_async( + async def get_role_statistics_with_options_async( self, - model_id: str, - version_name: str, + request: aiwork_space_20210204_models.GetRoleStatisticsRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.GetModelVersionResponse: + ) -> aiwork_space_20210204_models.GetRoleStatisticsResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.workspace_id): + query['WorkspaceId'] = request.workspace_id req = open_api_models.OpenApiRequest( - headers=headers + headers=headers, + query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='GetModelVersion', + action='GetRoleStatistics', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/models/{OpenApiUtilClient.get_encode_param(model_id)}/versions/{OpenApiUtilClient.get_encode_param(version_name)}', + pathname=f'/api/v1/statistics/roles', method='GET', auth_type='AK', style='ROA', @@ -3572,51 +5062,40 @@ async def get_model_version_with_options_async( body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.GetModelVersionResponse(), + aiwork_space_20210204_models.GetRoleStatisticsResponse(), await self.call_api_async(params, req, runtime) ) - def get_model_version( + def get_role_statistics( self, - model_id: str, - version_name: str, - ) -> aiwork_space_20210204_models.GetModelVersionResponse: + request: aiwork_space_20210204_models.GetRoleStatisticsRequest, + ) -> aiwork_space_20210204_models.GetRoleStatisticsResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.get_model_version_with_options(model_id, version_name, headers, runtime) + return self.get_role_statistics_with_options(request, headers, runtime) - async def get_model_version_async( + async def get_role_statistics_async( self, - model_id: str, - version_name: str, - ) -> aiwork_space_20210204_models.GetModelVersionResponse: + request: aiwork_space_20210204_models.GetRoleStatisticsRequest, + ) -> aiwork_space_20210204_models.GetRoleStatisticsResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.get_model_version_with_options_async(model_id, version_name, headers, runtime) + return await self.get_role_statistics_with_options_async(request, headers, runtime) - def get_permission_with_options( + def get_service_template_with_options( self, - workspace_id: str, - permission_code: str, - request: aiwork_space_20210204_models.GetPermissionRequest, + service_template_id: str, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.GetPermissionResponse: - UtilClient.validate_model(request) - query = {} - if not UtilClient.is_unset(request.accessibility): - query['Accessibility'] = request.accessibility - if not UtilClient.is_unset(request.creator): - query['Creator'] = request.creator + ) -> aiwork_space_20210204_models.GetServiceTemplateResponse: req = open_api_models.OpenApiRequest( - headers=headers, - query=OpenApiUtilClient.query(query) + headers=headers ) params = open_api_models.Params( - action='GetPermission', + action='GetServiceTemplate', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/workspaces/{OpenApiUtilClient.get_encode_param(workspace_id)}/permissions/{OpenApiUtilClient.get_encode_param(permission_code)}', + pathname=f'/api/v1/servicetemplates/{OpenApiUtilClient.get_encode_param(service_template_id)}', method='GET', auth_type='AK', style='ROA', @@ -3624,33 +5103,24 @@ def get_permission_with_options( body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.GetPermissionResponse(), + aiwork_space_20210204_models.GetServiceTemplateResponse(), self.call_api(params, req, runtime) ) - async def get_permission_with_options_async( + async def get_service_template_with_options_async( self, - workspace_id: str, - permission_code: str, - request: aiwork_space_20210204_models.GetPermissionRequest, + service_template_id: str, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.GetPermissionResponse: - UtilClient.validate_model(request) - query = {} - if not UtilClient.is_unset(request.accessibility): - query['Accessibility'] = request.accessibility - if not UtilClient.is_unset(request.creator): - query['Creator'] = request.creator + ) -> aiwork_space_20210204_models.GetServiceTemplateResponse: req = open_api_models.OpenApiRequest( - headers=headers, - query=OpenApiUtilClient.query(query) + headers=headers ) params = open_api_models.Params( - action='GetPermission', + action='GetServiceTemplate', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/workspaces/{OpenApiUtilClient.get_encode_param(workspace_id)}/permissions/{OpenApiUtilClient.get_encode_param(permission_code)}', + pathname=f'/api/v1/servicetemplates/{OpenApiUtilClient.get_encode_param(service_template_id)}', method='GET', auth_type='AK', style='ROA', @@ -3658,49 +5128,40 @@ async def get_permission_with_options_async( body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.GetPermissionResponse(), + aiwork_space_20210204_models.GetServiceTemplateResponse(), await self.call_api_async(params, req, runtime) ) - def get_permission( + def get_service_template( self, - workspace_id: str, - permission_code: str, - request: aiwork_space_20210204_models.GetPermissionRequest, - ) -> aiwork_space_20210204_models.GetPermissionResponse: + service_template_id: str, + ) -> aiwork_space_20210204_models.GetServiceTemplateResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.get_permission_with_options(workspace_id, permission_code, request, headers, runtime) + return self.get_service_template_with_options(service_template_id, headers, runtime) - async def get_permission_async( + async def get_service_template_async( self, - workspace_id: str, - permission_code: str, - request: aiwork_space_20210204_models.GetPermissionRequest, - ) -> aiwork_space_20210204_models.GetPermissionResponse: + service_template_id: str, + ) -> aiwork_space_20210204_models.GetServiceTemplateResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.get_permission_with_options_async(workspace_id, permission_code, request, headers, runtime) + return await self.get_service_template_with_options_async(service_template_id, headers, runtime) - def get_role_statistics_with_options( + def get_trial_with_options( self, - request: aiwork_space_20210204_models.GetRoleStatisticsRequest, + trial_id: str, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.GetRoleStatisticsResponse: - UtilClient.validate_model(request) - query = {} - if not UtilClient.is_unset(request.workspace_id): - query['WorkspaceId'] = request.workspace_id + ) -> aiwork_space_20210204_models.GetTrialResponse: req = open_api_models.OpenApiRequest( - headers=headers, - query=OpenApiUtilClient.query(query) + headers=headers ) params = open_api_models.Params( - action='GetRoleStatistics', + action='GetTrial', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/statistics/roles', + pathname=f'/api/v1/trials/{OpenApiUtilClient.get_encode_param(trial_id)}', method='GET', auth_type='AK', style='ROA', @@ -3708,29 +5169,24 @@ def get_role_statistics_with_options( body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.GetRoleStatisticsResponse(), + aiwork_space_20210204_models.GetTrialResponse(), self.call_api(params, req, runtime) ) - async def get_role_statistics_with_options_async( + async def get_trial_with_options_async( self, - request: aiwork_space_20210204_models.GetRoleStatisticsRequest, + trial_id: str, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.GetRoleStatisticsResponse: - UtilClient.validate_model(request) - query = {} - if not UtilClient.is_unset(request.workspace_id): - query['WorkspaceId'] = request.workspace_id + ) -> aiwork_space_20210204_models.GetTrialResponse: req = open_api_models.OpenApiRequest( - headers=headers, - query=OpenApiUtilClient.query(query) + headers=headers ) params = open_api_models.Params( - action='GetRoleStatistics', + action='GetTrial', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/statistics/roles', + pathname=f'/api/v1/trials/{OpenApiUtilClient.get_encode_param(trial_id)}', method='GET', auth_type='AK', style='ROA', @@ -3738,25 +5194,25 @@ async def get_role_statistics_with_options_async( body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.GetRoleStatisticsResponse(), + aiwork_space_20210204_models.GetTrialResponse(), await self.call_api_async(params, req, runtime) ) - def get_role_statistics( + def get_trial( self, - request: aiwork_space_20210204_models.GetRoleStatisticsRequest, - ) -> aiwork_space_20210204_models.GetRoleStatisticsResponse: + trial_id: str, + ) -> aiwork_space_20210204_models.GetTrialResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.get_role_statistics_with_options(request, headers, runtime) + return self.get_trial_with_options(trial_id, headers, runtime) - async def get_role_statistics_async( + async def get_trial_async( self, - request: aiwork_space_20210204_models.GetRoleStatisticsRequest, - ) -> aiwork_space_20210204_models.GetRoleStatisticsResponse: + trial_id: str, + ) -> aiwork_space_20210204_models.GetTrialResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.get_role_statistics_with_options_async(request, headers, runtime) + return await self.get_trial_with_options_async(trial_id, headers, runtime) def get_workspace_with_options( self, @@ -3934,6 +5390,86 @@ async def list_code_sources_async( headers = {} return await self.list_code_sources_with_options_async(request, headers, runtime) + def list_collections_with_options( + self, + request: aiwork_space_20210204_models.ListCollectionsRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.ListCollectionsResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.page_number): + query['PageNumber'] = request.page_number + if not UtilClient.is_unset(request.page_size): + query['PageSize'] = request.page_size + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='ListCollections', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/collections', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.ListCollectionsResponse(), + self.call_api(params, req, runtime) + ) + + async def list_collections_with_options_async( + self, + request: aiwork_space_20210204_models.ListCollectionsRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.ListCollectionsResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.page_number): + query['PageNumber'] = request.page_number + if not UtilClient.is_unset(request.page_size): + query['PageSize'] = request.page_size + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='ListCollections', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/collections', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.ListCollectionsResponse(), + await self.call_api_async(params, req, runtime) + ) + + def list_collections( + self, + request: aiwork_space_20210204_models.ListCollectionsRequest, + ) -> aiwork_space_20210204_models.ListCollectionsResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.list_collections_with_options(request, headers, runtime) + + async def list_collections_async( + self, + request: aiwork_space_20210204_models.ListCollectionsRequest, + ) -> aiwork_space_20210204_models.ListCollectionsResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.list_collections_with_options_async(request, headers, runtime) + def list_configs_with_options( self, workspace_id: str, @@ -3945,6 +5481,10 @@ def list_configs_with_options( query = {} if not UtilClient.is_unset(request.config_keys): query['ConfigKeys'] = request.config_keys + if not UtilClient.is_unset(request.labels): + query['Labels'] = request.labels + if not UtilClient.is_unset(request.verbose): + query['Verbose'] = request.verbose req = open_api_models.OpenApiRequest( headers=headers, query=OpenApiUtilClient.query(query) @@ -3976,6 +5516,10 @@ async def list_configs_with_options_async( query = {} if not UtilClient.is_unset(request.config_keys): query['ConfigKeys'] = request.config_keys + if not UtilClient.is_unset(request.labels): + query['Labels'] = request.labels + if not UtilClient.is_unset(request.verbose): + query['Verbose'] = request.verbose req = open_api_models.OpenApiRequest( headers=headers, query=OpenApiUtilClient.query(query) @@ -4042,6 +5586,8 @@ def list_datasets_with_options( query['PageSize'] = request.page_size if not UtilClient.is_unset(request.properties): query['Properties'] = request.properties + if not UtilClient.is_unset(request.provider): + query['Provider'] = request.provider if not UtilClient.is_unset(request.source_id): query['SourceId'] = request.source_id if not UtilClient.is_unset(request.source_types): @@ -4094,12 +5640,114 @@ async def list_datasets_with_options_async( query['PageNumber'] = request.page_number if not UtilClient.is_unset(request.page_size): query['PageSize'] = request.page_size - if not UtilClient.is_unset(request.properties): - query['Properties'] = request.properties - if not UtilClient.is_unset(request.source_id): - query['SourceId'] = request.source_id - if not UtilClient.is_unset(request.source_types): - query['SourceTypes'] = request.source_types + if not UtilClient.is_unset(request.properties): + query['Properties'] = request.properties + if not UtilClient.is_unset(request.provider): + query['Provider'] = request.provider + if not UtilClient.is_unset(request.source_id): + query['SourceId'] = request.source_id + if not UtilClient.is_unset(request.source_types): + query['SourceTypes'] = request.source_types + if not UtilClient.is_unset(request.workspace_id): + query['WorkspaceId'] = request.workspace_id + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='ListDatasets', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/datasets', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.ListDatasetsResponse(), + await self.call_api_async(params, req, runtime) + ) + + def list_datasets( + self, + request: aiwork_space_20210204_models.ListDatasetsRequest, + ) -> aiwork_space_20210204_models.ListDatasetsResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.list_datasets_with_options(request, headers, runtime) + + async def list_datasets_async( + self, + request: aiwork_space_20210204_models.ListDatasetsRequest, + ) -> aiwork_space_20210204_models.ListDatasetsResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.list_datasets_with_options_async(request, headers, runtime) + + def list_experiment_with_options( + self, + request: aiwork_space_20210204_models.ListExperimentRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.ListExperimentResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.labels): + query['Labels'] = request.labels + if not UtilClient.is_unset(request.name): + query['Name'] = request.name + if not UtilClient.is_unset(request.order): + query['Order'] = request.order + if not UtilClient.is_unset(request.page_number): + query['PageNumber'] = request.page_number + if not UtilClient.is_unset(request.page_size): + query['PageSize'] = request.page_size + if not UtilClient.is_unset(request.sort_by): + query['SortBy'] = request.sort_by + if not UtilClient.is_unset(request.workspace_id): + query['WorkspaceId'] = request.workspace_id + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='ListExperiment', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/experiments', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.ListExperimentResponse(), + self.call_api(params, req, runtime) + ) + + async def list_experiment_with_options_async( + self, + request: aiwork_space_20210204_models.ListExperimentRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.ListExperimentResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.labels): + query['Labels'] = request.labels + if not UtilClient.is_unset(request.name): + query['Name'] = request.name + if not UtilClient.is_unset(request.order): + query['Order'] = request.order + if not UtilClient.is_unset(request.page_number): + query['PageNumber'] = request.page_number + if not UtilClient.is_unset(request.page_size): + query['PageSize'] = request.page_size + if not UtilClient.is_unset(request.sort_by): + query['SortBy'] = request.sort_by if not UtilClient.is_unset(request.workspace_id): query['WorkspaceId'] = request.workspace_id req = open_api_models.OpenApiRequest( @@ -4107,10 +5755,10 @@ async def list_datasets_with_options_async( query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='ListDatasets', + action='ListExperiment', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/datasets', + pathname=f'/api/v1/experiments', method='GET', auth_type='AK', style='ROA', @@ -4118,25 +5766,25 @@ async def list_datasets_with_options_async( body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.ListDatasetsResponse(), + aiwork_space_20210204_models.ListExperimentResponse(), await self.call_api_async(params, req, runtime) ) - def list_datasets( + def list_experiment( self, - request: aiwork_space_20210204_models.ListDatasetsRequest, - ) -> aiwork_space_20210204_models.ListDatasetsResponse: + request: aiwork_space_20210204_models.ListExperimentRequest, + ) -> aiwork_space_20210204_models.ListExperimentResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.list_datasets_with_options(request, headers, runtime) + return self.list_experiment_with_options(request, headers, runtime) - async def list_datasets_async( + async def list_experiment_async( self, - request: aiwork_space_20210204_models.ListDatasetsRequest, - ) -> aiwork_space_20210204_models.ListDatasetsResponse: + request: aiwork_space_20210204_models.ListExperimentRequest, + ) -> aiwork_space_20210204_models.ListExperimentResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.list_datasets_with_options_async(request, headers, runtime) + return await self.list_experiment_with_options_async(request, headers, runtime) def list_features_with_options( self, @@ -4272,6 +5920,82 @@ async def list_global_permissions_async(self) -> aiwork_space_20210204_models.Li headers = {} return await self.list_global_permissions_with_options_async(headers, runtime) + def list_image_label_keys_with_options( + self, + request: aiwork_space_20210204_models.ListImageLabelKeysRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.ListImageLabelKeysResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.label_key_prefixes): + query['LabelKeyPrefixes'] = request.label_key_prefixes + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='ListImageLabelKeys', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/image/labelkeys', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.ListImageLabelKeysResponse(), + self.call_api(params, req, runtime) + ) + + async def list_image_label_keys_with_options_async( + self, + request: aiwork_space_20210204_models.ListImageLabelKeysRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.ListImageLabelKeysResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.label_key_prefixes): + query['LabelKeyPrefixes'] = request.label_key_prefixes + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='ListImageLabelKeys', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/image/labelkeys', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.ListImageLabelKeysResponse(), + await self.call_api_async(params, req, runtime) + ) + + def list_image_label_keys( + self, + request: aiwork_space_20210204_models.ListImageLabelKeysRequest, + ) -> aiwork_space_20210204_models.ListImageLabelKeysResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.list_image_label_keys_with_options(request, headers, runtime) + + async def list_image_label_keys_async( + self, + request: aiwork_space_20210204_models.ListImageLabelKeysRequest, + ) -> aiwork_space_20210204_models.ListImageLabelKeysResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.list_image_label_keys_with_options_async(request, headers, runtime) + def list_image_labels_with_options( self, request: aiwork_space_20210204_models.ListImageLabelsRequest, @@ -4372,6 +6096,8 @@ def list_images_with_options( ) -> aiwork_space_20210204_models.ListImagesResponse: UtilClient.validate_model(request) query = {} + if not UtilClient.is_unset(request.accessibility): + query['Accessibility'] = request.accessibility if not UtilClient.is_unset(request.labels): query['Labels'] = request.labels if not UtilClient.is_unset(request.name): @@ -4422,6 +6148,8 @@ async def list_images_with_options_async( ) -> aiwork_space_20210204_models.ListImagesResponse: UtilClient.validate_model(request) query = {} + if not UtilClient.is_unset(request.accessibility): + query['Accessibility'] = request.accessibility if not UtilClient.is_unset(request.labels): query['Labels'] = request.labels if not UtilClient.is_unset(request.name): @@ -4784,6 +6512,8 @@ def list_models_with_options( ) -> aiwork_space_20210204_models.ListModelsResponse: UtilClient.validate_model(request) query = {} + if not UtilClient.is_unset(request.collections): + query['Collections'] = request.collections if not UtilClient.is_unset(request.domain): query['Domain'] = request.domain if not UtilClient.is_unset(request.label): @@ -4794,6 +6524,8 @@ def list_models_with_options( query['Labels'] = request.labels if not UtilClient.is_unset(request.model_name): query['ModelName'] = request.model_name + if not UtilClient.is_unset(request.model_type): + query['ModelType'] = request.model_type if not UtilClient.is_unset(request.order): query['Order'] = request.order if not UtilClient.is_unset(request.origin): @@ -4840,6 +6572,8 @@ async def list_models_with_options_async( ) -> aiwork_space_20210204_models.ListModelsResponse: UtilClient.validate_model(request) query = {} + if not UtilClient.is_unset(request.collections): + query['Collections'] = request.collections if not UtilClient.is_unset(request.domain): query['Domain'] = request.domain if not UtilClient.is_unset(request.label): @@ -4850,6 +6584,8 @@ async def list_models_with_options_async( query['Labels'] = request.labels if not UtilClient.is_unset(request.model_name): query['ModelName'] = request.model_name + if not UtilClient.is_unset(request.model_type): + query['ModelType'] = request.model_type if not UtilClient.is_unset(request.order): query['Order'] = request.order if not UtilClient.is_unset(request.origin): @@ -5404,6 +7140,60 @@ def list_resources_with_options( query = {} if not UtilClient.is_unset(request.group_name): query['GroupName'] = request.group_name + if not UtilClient.is_unset(request.labels): + query['Labels'] = request.labels + if not UtilClient.is_unset(request.option): + query['Option'] = request.option + if not UtilClient.is_unset(request.page_number): + query['PageNumber'] = request.page_number + if not UtilClient.is_unset(request.page_size): + query['PageSize'] = request.page_size + if not UtilClient.is_unset(request.product_types): + query['ProductTypes'] = request.product_types + if not UtilClient.is_unset(request.quota_ids): + query['QuotaIds'] = request.quota_ids + if not UtilClient.is_unset(request.resource_name): + query['ResourceName'] = request.resource_name + if not UtilClient.is_unset(request.resource_types): + query['ResourceTypes'] = request.resource_types + if not UtilClient.is_unset(request.verbose): + query['Verbose'] = request.verbose + if not UtilClient.is_unset(request.verbose_fields): + query['VerboseFields'] = request.verbose_fields + if not UtilClient.is_unset(request.workspace_id): + query['WorkspaceId'] = request.workspace_id + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='ListResources', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/resources', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.ListResourcesResponse(), + self.call_api(params, req, runtime) + ) + + async def list_resources_with_options_async( + self, + request: aiwork_space_20210204_models.ListResourcesRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.ListResourcesResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.group_name): + query['GroupName'] = request.group_name + if not UtilClient.is_unset(request.labels): + query['Labels'] = request.labels if not UtilClient.is_unset(request.option): query['Option'] = request.option if not UtilClient.is_unset(request.page_number): @@ -5412,12 +7202,16 @@ def list_resources_with_options( query['PageSize'] = request.page_size if not UtilClient.is_unset(request.product_types): query['ProductTypes'] = request.product_types + if not UtilClient.is_unset(request.quota_ids): + query['QuotaIds'] = request.quota_ids if not UtilClient.is_unset(request.resource_name): query['ResourceName'] = request.resource_name if not UtilClient.is_unset(request.resource_types): query['ResourceTypes'] = request.resource_types if not UtilClient.is_unset(request.verbose): query['Verbose'] = request.verbose + if not UtilClient.is_unset(request.verbose_fields): + query['VerboseFields'] = request.verbose_fields if not UtilClient.is_unset(request.workspace_id): query['WorkspaceId'] = request.workspace_id req = open_api_models.OpenApiRequest( @@ -5425,10 +7219,162 @@ def list_resources_with_options( query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='ListResources', + action='ListResources', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/resources', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.ListResourcesResponse(), + await self.call_api_async(params, req, runtime) + ) + + def list_resources( + self, + request: aiwork_space_20210204_models.ListResourcesRequest, + ) -> aiwork_space_20210204_models.ListResourcesResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.list_resources_with_options(request, headers, runtime) + + async def list_resources_async( + self, + request: aiwork_space_20210204_models.ListResourcesRequest, + ) -> aiwork_space_20210204_models.ListResourcesResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.list_resources_with_options_async(request, headers, runtime) + + def list_service_templates_with_options( + self, + request: aiwork_space_20210204_models.ListServiceTemplatesRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.ListServiceTemplatesResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.label): + query['Label'] = request.label + if not UtilClient.is_unset(request.order): + query['Order'] = request.order + if not UtilClient.is_unset(request.page_number): + query['PageNumber'] = request.page_number + if not UtilClient.is_unset(request.page_size): + query['PageSize'] = request.page_size + if not UtilClient.is_unset(request.provider): + query['Provider'] = request.provider + if not UtilClient.is_unset(request.query): + query['Query'] = request.query + if not UtilClient.is_unset(request.service_template_name): + query['ServiceTemplateName'] = request.service_template_name + if not UtilClient.is_unset(request.sort_by): + query['SortBy'] = request.sort_by + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='ListServiceTemplates', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/servicetemplates', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.ListServiceTemplatesResponse(), + self.call_api(params, req, runtime) + ) + + async def list_service_templates_with_options_async( + self, + request: aiwork_space_20210204_models.ListServiceTemplatesRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.ListServiceTemplatesResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.label): + query['Label'] = request.label + if not UtilClient.is_unset(request.order): + query['Order'] = request.order + if not UtilClient.is_unset(request.page_number): + query['PageNumber'] = request.page_number + if not UtilClient.is_unset(request.page_size): + query['PageSize'] = request.page_size + if not UtilClient.is_unset(request.provider): + query['Provider'] = request.provider + if not UtilClient.is_unset(request.query): + query['Query'] = request.query + if not UtilClient.is_unset(request.service_template_name): + query['ServiceTemplateName'] = request.service_template_name + if not UtilClient.is_unset(request.sort_by): + query['SortBy'] = request.sort_by + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='ListServiceTemplates', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/servicetemplates', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.ListServiceTemplatesResponse(), + await self.call_api_async(params, req, runtime) + ) + + def list_service_templates( + self, + request: aiwork_space_20210204_models.ListServiceTemplatesRequest, + ) -> aiwork_space_20210204_models.ListServiceTemplatesResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.list_service_templates_with_options(request, headers, runtime) + + async def list_service_templates_async( + self, + request: aiwork_space_20210204_models.ListServiceTemplatesRequest, + ) -> aiwork_space_20210204_models.ListServiceTemplatesResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.list_service_templates_with_options_async(request, headers, runtime) + + def list_user_configs_with_options( + self, + request: aiwork_space_20210204_models.ListUserConfigsRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.ListUserConfigsResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.category_names): + query['CategoryNames'] = request.category_names + if not UtilClient.is_unset(request.config_keys): + query['ConfigKeys'] = request.config_keys + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='ListUserConfigs', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/resources', + pathname=f'/api/v1/userconfigs', method='GET', auth_type='AK', style='ROA', @@ -5436,45 +7382,31 @@ def list_resources_with_options( body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.ListResourcesResponse(), + aiwork_space_20210204_models.ListUserConfigsResponse(), self.call_api(params, req, runtime) ) - async def list_resources_with_options_async( + async def list_user_configs_with_options_async( self, - request: aiwork_space_20210204_models.ListResourcesRequest, + request: aiwork_space_20210204_models.ListUserConfigsRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.ListResourcesResponse: + ) -> aiwork_space_20210204_models.ListUserConfigsResponse: UtilClient.validate_model(request) query = {} - if not UtilClient.is_unset(request.group_name): - query['GroupName'] = request.group_name - if not UtilClient.is_unset(request.option): - query['Option'] = request.option - if not UtilClient.is_unset(request.page_number): - query['PageNumber'] = request.page_number - if not UtilClient.is_unset(request.page_size): - query['PageSize'] = request.page_size - if not UtilClient.is_unset(request.product_types): - query['ProductTypes'] = request.product_types - if not UtilClient.is_unset(request.resource_name): - query['ResourceName'] = request.resource_name - if not UtilClient.is_unset(request.resource_types): - query['ResourceTypes'] = request.resource_types - if not UtilClient.is_unset(request.verbose): - query['Verbose'] = request.verbose - if not UtilClient.is_unset(request.workspace_id): - query['WorkspaceId'] = request.workspace_id + if not UtilClient.is_unset(request.category_names): + query['CategoryNames'] = request.category_names + if not UtilClient.is_unset(request.config_keys): + query['ConfigKeys'] = request.config_keys req = open_api_models.OpenApiRequest( headers=headers, query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='ListResources', + action='ListUserConfigs', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/resources', + pathname=f'/api/v1/userconfigs', method='GET', auth_type='AK', style='ROA', @@ -5482,25 +7414,25 @@ async def list_resources_with_options_async( body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.ListResourcesResponse(), + aiwork_space_20210204_models.ListUserConfigsResponse(), await self.call_api_async(params, req, runtime) ) - def list_resources( + def list_user_configs( self, - request: aiwork_space_20210204_models.ListResourcesRequest, - ) -> aiwork_space_20210204_models.ListResourcesResponse: + request: aiwork_space_20210204_models.ListUserConfigsRequest, + ) -> aiwork_space_20210204_models.ListUserConfigsResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.list_resources_with_options(request, headers, runtime) + return self.list_user_configs_with_options(request, headers, runtime) - async def list_resources_async( + async def list_user_configs_async( self, - request: aiwork_space_20210204_models.ListResourcesRequest, - ) -> aiwork_space_20210204_models.ListResourcesResponse: + request: aiwork_space_20210204_models.ListUserConfigsRequest, + ) -> aiwork_space_20210204_models.ListUserConfigsResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.list_resources_with_options_async(request, headers, runtime) + return await self.list_user_configs_with_options_async(request, headers, runtime) def list_users_with_options( self, @@ -5597,11 +7529,17 @@ async def list_users_async( def list_workspace_users_with_options( self, workspace_id: str, + request: aiwork_space_20210204_models.ListWorkspaceUsersRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> aiwork_space_20210204_models.ListWorkspaceUsersResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.user_name): + query['UserName'] = request.user_name req = open_api_models.OpenApiRequest( - headers=headers + headers=headers, + query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( action='ListWorkspaceUsers', @@ -5622,11 +7560,17 @@ def list_workspace_users_with_options( async def list_workspace_users_with_options_async( self, workspace_id: str, + request: aiwork_space_20210204_models.ListWorkspaceUsersRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> aiwork_space_20210204_models.ListWorkspaceUsersResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.user_name): + query['UserName'] = request.user_name req = open_api_models.OpenApiRequest( - headers=headers + headers=headers, + query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( action='ListWorkspaceUsers', @@ -5647,18 +7591,20 @@ async def list_workspace_users_with_options_async( def list_workspace_users( self, workspace_id: str, + request: aiwork_space_20210204_models.ListWorkspaceUsersRequest, ) -> aiwork_space_20210204_models.ListWorkspaceUsersResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.list_workspace_users_with_options(workspace_id, headers, runtime) + return self.list_workspace_users_with_options(workspace_id, request, headers, runtime) async def list_workspace_users_async( self, workspace_id: str, + request: aiwork_space_20210204_models.ListWorkspaceUsersRequest, ) -> aiwork_space_20210204_models.ListWorkspaceUsersResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.list_workspace_users_with_options_async(workspace_id, headers, runtime) + return await self.list_workspace_users_with_options_async(workspace_id, request, headers, runtime) def list_workspaces_with_options( self, @@ -6122,229 +8068,465 @@ def remove_image( ) -> aiwork_space_20210204_models.RemoveImageResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.remove_image_with_options(image_id, headers, runtime) + return self.remove_image_with_options(image_id, headers, runtime) + + async def remove_image_async( + self, + image_id: str, + ) -> aiwork_space_20210204_models.RemoveImageResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.remove_image_with_options_async(image_id, headers, runtime) + + def remove_image_labels_with_options( + self, + image_id: str, + label_key: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.RemoveImageLabelsResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='RemoveImageLabels', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/images/{OpenApiUtilClient.get_encode_param(image_id)}/labels/{OpenApiUtilClient.get_encode_param(label_key)}', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.RemoveImageLabelsResponse(), + self.call_api(params, req, runtime) + ) + + async def remove_image_labels_with_options_async( + self, + image_id: str, + label_key: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.RemoveImageLabelsResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='RemoveImageLabels', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/images/{OpenApiUtilClient.get_encode_param(image_id)}/labels/{OpenApiUtilClient.get_encode_param(label_key)}', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.RemoveImageLabelsResponse(), + await self.call_api_async(params, req, runtime) + ) + + def remove_image_labels( + self, + image_id: str, + label_key: str, + ) -> aiwork_space_20210204_models.RemoveImageLabelsResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.remove_image_labels_with_options(image_id, label_key, headers, runtime) + + async def remove_image_labels_async( + self, + image_id: str, + label_key: str, + ) -> aiwork_space_20210204_models.RemoveImageLabelsResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.remove_image_labels_with_options_async(image_id, label_key, headers, runtime) + + def remove_member_role_with_options( + self, + workspace_id: str, + member_id: str, + role_name: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.RemoveMemberRoleResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='RemoveMemberRole', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/workspaces/{OpenApiUtilClient.get_encode_param(workspace_id)}/members/{OpenApiUtilClient.get_encode_param(member_id)}/roles/{OpenApiUtilClient.get_encode_param(role_name)}', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.RemoveMemberRoleResponse(), + self.call_api(params, req, runtime) + ) + + async def remove_member_role_with_options_async( + self, + workspace_id: str, + member_id: str, + role_name: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.RemoveMemberRoleResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='RemoveMemberRole', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/workspaces/{OpenApiUtilClient.get_encode_param(workspace_id)}/members/{OpenApiUtilClient.get_encode_param(member_id)}/roles/{OpenApiUtilClient.get_encode_param(role_name)}', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.RemoveMemberRoleResponse(), + await self.call_api_async(params, req, runtime) + ) + + def remove_member_role( + self, + workspace_id: str, + member_id: str, + role_name: str, + ) -> aiwork_space_20210204_models.RemoveMemberRoleResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.remove_member_role_with_options(workspace_id, member_id, role_name, headers, runtime) + + async def remove_member_role_async( + self, + workspace_id: str, + member_id: str, + role_name: str, + ) -> aiwork_space_20210204_models.RemoveMemberRoleResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.remove_member_role_with_options_async(workspace_id, member_id, role_name, headers, runtime) + + def remove_workspace_quota_with_options( + self, + workspace_id: str, + quota_id: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.RemoveWorkspaceQuotaResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='RemoveWorkspaceQuota', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/workspaces/{OpenApiUtilClient.get_encode_param(workspace_id)}/quotas/{OpenApiUtilClient.get_encode_param(quota_id)}', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.RemoveWorkspaceQuotaResponse(), + self.call_api(params, req, runtime) + ) + + async def remove_workspace_quota_with_options_async( + self, + workspace_id: str, + quota_id: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.RemoveWorkspaceQuotaResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='RemoveWorkspaceQuota', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/workspaces/{OpenApiUtilClient.get_encode_param(workspace_id)}/quotas/{OpenApiUtilClient.get_encode_param(quota_id)}', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.RemoveWorkspaceQuotaResponse(), + await self.call_api_async(params, req, runtime) + ) + + def remove_workspace_quota( + self, + workspace_id: str, + quota_id: str, + ) -> aiwork_space_20210204_models.RemoveWorkspaceQuotaResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.remove_workspace_quota_with_options(workspace_id, quota_id, headers, runtime) - async def remove_image_async( + async def remove_workspace_quota_async( self, - image_id: str, - ) -> aiwork_space_20210204_models.RemoveImageResponse: + workspace_id: str, + quota_id: str, + ) -> aiwork_space_20210204_models.RemoveWorkspaceQuotaResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.remove_image_with_options_async(image_id, headers, runtime) + return await self.remove_workspace_quota_with_options_async(workspace_id, quota_id, headers, runtime) - def remove_image_labels_with_options( + def set_experiment_labels_with_options( self, - image_id: str, - label_keys: str, + experiment_id: str, + request: aiwork_space_20210204_models.SetExperimentLabelsRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.RemoveImageLabelsResponse: + ) -> aiwork_space_20210204_models.SetExperimentLabelsResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.labels): + body['Labels'] = request.labels req = open_api_models.OpenApiRequest( - headers=headers + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) ) params = open_api_models.Params( - action='RemoveImageLabels', + action='SetExperimentLabels', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/images/{OpenApiUtilClient.get_encode_param(image_id)}/labels/{OpenApiUtilClient.get_encode_param(label_keys)}', - method='DELETE', + pathname=f'/api/v1/experiments/{OpenApiUtilClient.get_encode_param(experiment_id)}/labels', + method='POST', auth_type='AK', style='ROA', req_body_type='json', body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.RemoveImageLabelsResponse(), + aiwork_space_20210204_models.SetExperimentLabelsResponse(), self.call_api(params, req, runtime) ) - async def remove_image_labels_with_options_async( + async def set_experiment_labels_with_options_async( self, - image_id: str, - label_keys: str, + experiment_id: str, + request: aiwork_space_20210204_models.SetExperimentLabelsRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.RemoveImageLabelsResponse: + ) -> aiwork_space_20210204_models.SetExperimentLabelsResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.labels): + body['Labels'] = request.labels req = open_api_models.OpenApiRequest( - headers=headers + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) ) params = open_api_models.Params( - action='RemoveImageLabels', + action='SetExperimentLabels', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/images/{OpenApiUtilClient.get_encode_param(image_id)}/labels/{OpenApiUtilClient.get_encode_param(label_keys)}', - method='DELETE', + pathname=f'/api/v1/experiments/{OpenApiUtilClient.get_encode_param(experiment_id)}/labels', + method='POST', auth_type='AK', style='ROA', req_body_type='json', body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.RemoveImageLabelsResponse(), + aiwork_space_20210204_models.SetExperimentLabelsResponse(), await self.call_api_async(params, req, runtime) ) - def remove_image_labels( + def set_experiment_labels( self, - image_id: str, - label_keys: str, - ) -> aiwork_space_20210204_models.RemoveImageLabelsResponse: + experiment_id: str, + request: aiwork_space_20210204_models.SetExperimentLabelsRequest, + ) -> aiwork_space_20210204_models.SetExperimentLabelsResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.remove_image_labels_with_options(image_id, label_keys, headers, runtime) + return self.set_experiment_labels_with_options(experiment_id, request, headers, runtime) - async def remove_image_labels_async( + async def set_experiment_labels_async( self, - image_id: str, - label_keys: str, - ) -> aiwork_space_20210204_models.RemoveImageLabelsResponse: + experiment_id: str, + request: aiwork_space_20210204_models.SetExperimentLabelsRequest, + ) -> aiwork_space_20210204_models.SetExperimentLabelsResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.remove_image_labels_with_options_async(image_id, label_keys, headers, runtime) + return await self.set_experiment_labels_with_options_async(experiment_id, request, headers, runtime) - def remove_member_role_with_options( + def set_trial_labels_with_options( self, - workspace_id: str, - member_id: str, - role_name: str, + trial_id: str, + request: aiwork_space_20210204_models.SetTrialLabelsRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.RemoveMemberRoleResponse: + ) -> aiwork_space_20210204_models.SetTrialLabelsResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.labels): + body['Labels'] = request.labels req = open_api_models.OpenApiRequest( - headers=headers + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) ) params = open_api_models.Params( - action='RemoveMemberRole', + action='SetTrialLabels', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/workspaces/{OpenApiUtilClient.get_encode_param(workspace_id)}/members/{OpenApiUtilClient.get_encode_param(member_id)}/roles/{OpenApiUtilClient.get_encode_param(role_name)}', - method='DELETE', + pathname=f'/api/v1/trials/{OpenApiUtilClient.get_encode_param(trial_id)}/Labels', + method='POST', auth_type='AK', style='ROA', req_body_type='json', body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.RemoveMemberRoleResponse(), + aiwork_space_20210204_models.SetTrialLabelsResponse(), self.call_api(params, req, runtime) ) - async def remove_member_role_with_options_async( + async def set_trial_labels_with_options_async( self, - workspace_id: str, - member_id: str, - role_name: str, + trial_id: str, + request: aiwork_space_20210204_models.SetTrialLabelsRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.RemoveMemberRoleResponse: + ) -> aiwork_space_20210204_models.SetTrialLabelsResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.labels): + body['Labels'] = request.labels req = open_api_models.OpenApiRequest( - headers=headers + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) ) params = open_api_models.Params( - action='RemoveMemberRole', + action='SetTrialLabels', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/workspaces/{OpenApiUtilClient.get_encode_param(workspace_id)}/members/{OpenApiUtilClient.get_encode_param(member_id)}/roles/{OpenApiUtilClient.get_encode_param(role_name)}', - method='DELETE', + pathname=f'/api/v1/trials/{OpenApiUtilClient.get_encode_param(trial_id)}/Labels', + method='POST', auth_type='AK', style='ROA', req_body_type='json', body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.RemoveMemberRoleResponse(), + aiwork_space_20210204_models.SetTrialLabelsResponse(), await self.call_api_async(params, req, runtime) ) - def remove_member_role( + def set_trial_labels( self, - workspace_id: str, - member_id: str, - role_name: str, - ) -> aiwork_space_20210204_models.RemoveMemberRoleResponse: + trial_id: str, + request: aiwork_space_20210204_models.SetTrialLabelsRequest, + ) -> aiwork_space_20210204_models.SetTrialLabelsResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.remove_member_role_with_options(workspace_id, member_id, role_name, headers, runtime) + return self.set_trial_labels_with_options(trial_id, request, headers, runtime) - async def remove_member_role_async( + async def set_trial_labels_async( self, - workspace_id: str, - member_id: str, - role_name: str, - ) -> aiwork_space_20210204_models.RemoveMemberRoleResponse: + trial_id: str, + request: aiwork_space_20210204_models.SetTrialLabelsRequest, + ) -> aiwork_space_20210204_models.SetTrialLabelsResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.remove_member_role_with_options_async(workspace_id, member_id, role_name, headers, runtime) + return await self.set_trial_labels_with_options_async(trial_id, request, headers, runtime) - def remove_workspace_quota_with_options( + def set_user_configs_with_options( self, - workspace_id: str, - quota_id: str, + request: aiwork_space_20210204_models.SetUserConfigsRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.RemoveWorkspaceQuotaResponse: + ) -> aiwork_space_20210204_models.SetUserConfigsResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.configs): + body['Configs'] = request.configs req = open_api_models.OpenApiRequest( - headers=headers + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) ) params = open_api_models.Params( - action='RemoveWorkspaceQuota', + action='SetUserConfigs', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/workspaces/{OpenApiUtilClient.get_encode_param(workspace_id)}/quotas/{OpenApiUtilClient.get_encode_param(quota_id)}', - method='DELETE', + pathname=f'/api/v1/userconfigs', + method='PUT', auth_type='AK', style='ROA', req_body_type='json', body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.RemoveWorkspaceQuotaResponse(), + aiwork_space_20210204_models.SetUserConfigsResponse(), self.call_api(params, req, runtime) ) - async def remove_workspace_quota_with_options_async( + async def set_user_configs_with_options_async( self, - workspace_id: str, - quota_id: str, + request: aiwork_space_20210204_models.SetUserConfigsRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> aiwork_space_20210204_models.RemoveWorkspaceQuotaResponse: + ) -> aiwork_space_20210204_models.SetUserConfigsResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.configs): + body['Configs'] = request.configs req = open_api_models.OpenApiRequest( - headers=headers + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) ) params = open_api_models.Params( - action='RemoveWorkspaceQuota', + action='SetUserConfigs', version='2021-02-04', protocol='HTTPS', - pathname=f'/api/v1/workspaces/{OpenApiUtilClient.get_encode_param(workspace_id)}/quotas/{OpenApiUtilClient.get_encode_param(quota_id)}', - method='DELETE', + pathname=f'/api/v1/userconfigs', + method='PUT', auth_type='AK', style='ROA', req_body_type='json', body_type='json' ) return TeaCore.from_map( - aiwork_space_20210204_models.RemoveWorkspaceQuotaResponse(), + aiwork_space_20210204_models.SetUserConfigsResponse(), await self.call_api_async(params, req, runtime) ) - def remove_workspace_quota( + def set_user_configs( self, - workspace_id: str, - quota_id: str, - ) -> aiwork_space_20210204_models.RemoveWorkspaceQuotaResponse: + request: aiwork_space_20210204_models.SetUserConfigsRequest, + ) -> aiwork_space_20210204_models.SetUserConfigsResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.remove_workspace_quota_with_options(workspace_id, quota_id, headers, runtime) + return self.set_user_configs_with_options(request, headers, runtime) - async def remove_workspace_quota_async( + async def set_user_configs_async( self, - workspace_id: str, - quota_id: str, - ) -> aiwork_space_20210204_models.RemoveWorkspaceQuotaResponse: + request: aiwork_space_20210204_models.SetUserConfigsRequest, + ) -> aiwork_space_20210204_models.SetUserConfigsResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.remove_workspace_quota_with_options_async(workspace_id, quota_id, headers, runtime) + return await self.set_user_configs_with_options_async(request, headers, runtime) def sync_users_with_options( self, @@ -6648,6 +8830,90 @@ async def update_default_workspace_async( headers = {} return await self.update_default_workspace_with_options_async(request, headers, runtime) + def update_experiment_with_options( + self, + experiment_id: str, + request: aiwork_space_20210204_models.UpdateExperimentRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.UpdateExperimentResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.accessibility): + body['Accessibility'] = request.accessibility + if not UtilClient.is_unset(request.name): + body['Name'] = request.name + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='UpdateExperiment', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/experiments/{OpenApiUtilClient.get_encode_param(experiment_id)}', + method='PUT', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.UpdateExperimentResponse(), + self.call_api(params, req, runtime) + ) + + async def update_experiment_with_options_async( + self, + experiment_id: str, + request: aiwork_space_20210204_models.UpdateExperimentRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.UpdateExperimentResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.accessibility): + body['Accessibility'] = request.accessibility + if not UtilClient.is_unset(request.name): + body['Name'] = request.name + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='UpdateExperiment', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/experiments/{OpenApiUtilClient.get_encode_param(experiment_id)}', + method='PUT', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.UpdateExperimentResponse(), + await self.call_api_async(params, req, runtime) + ) + + def update_experiment( + self, + experiment_id: str, + request: aiwork_space_20210204_models.UpdateExperimentRequest, + ) -> aiwork_space_20210204_models.UpdateExperimentResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.update_experiment_with_options(experiment_id, request, headers, runtime) + + async def update_experiment_async( + self, + experiment_id: str, + request: aiwork_space_20210204_models.UpdateExperimentRequest, + ) -> aiwork_space_20210204_models.UpdateExperimentResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.update_experiment_with_options_async(experiment_id, request, headers, runtime) + def update_model_with_options( self, model_id: str, @@ -6661,12 +8927,18 @@ def update_model_with_options( body['Accessibility'] = request.accessibility if not UtilClient.is_unset(request.domain): body['Domain'] = request.domain + if not UtilClient.is_unset(request.extra_info): + body['ExtraInfo'] = request.extra_info if not UtilClient.is_unset(request.model_description): body['ModelDescription'] = request.model_description if not UtilClient.is_unset(request.model_doc): body['ModelDoc'] = request.model_doc if not UtilClient.is_unset(request.model_name): body['ModelName'] = request.model_name + if not UtilClient.is_unset(request.model_type): + body['ModelType'] = request.model_type + if not UtilClient.is_unset(request.order_number): + body['OrderNumber'] = request.order_number if not UtilClient.is_unset(request.origin): body['Origin'] = request.origin if not UtilClient.is_unset(request.task): @@ -6704,12 +8976,18 @@ async def update_model_with_options_async( body['Accessibility'] = request.accessibility if not UtilClient.is_unset(request.domain): body['Domain'] = request.domain + if not UtilClient.is_unset(request.extra_info): + body['ExtraInfo'] = request.extra_info if not UtilClient.is_unset(request.model_description): body['ModelDescription'] = request.model_description if not UtilClient.is_unset(request.model_doc): body['ModelDoc'] = request.model_doc if not UtilClient.is_unset(request.model_name): body['ModelName'] = request.model_name + if not UtilClient.is_unset(request.model_type): + body['ModelType'] = request.model_type + if not UtilClient.is_unset(request.order_number): + body['OrderNumber'] = request.order_number if not UtilClient.is_unset(request.origin): body['Origin'] = request.origin if not UtilClient.is_unset(request.task): @@ -6840,6 +9118,10 @@ def update_model_version_with_options( body = {} if not UtilClient.is_unset(request.approval_status): body['ApprovalStatus'] = request.approval_status + if not UtilClient.is_unset(request.evaluation_spec): + body['EvaluationSpec'] = request.evaluation_spec + if not UtilClient.is_unset(request.extra_info): + body['ExtraInfo'] = request.extra_info if not UtilClient.is_unset(request.inference_spec): body['InferenceSpec'] = request.inference_spec if not UtilClient.is_unset(request.metrics): @@ -6886,6 +9168,10 @@ async def update_model_version_with_options_async( body = {} if not UtilClient.is_unset(request.approval_status): body['ApprovalStatus'] = request.approval_status + if not UtilClient.is_unset(request.evaluation_spec): + body['EvaluationSpec'] = request.evaluation_spec + if not UtilClient.is_unset(request.extra_info): + body['ExtraInfo'] = request.extra_info if not UtilClient.is_unset(request.inference_spec): body['InferenceSpec'] = request.inference_spec if not UtilClient.is_unset(request.metrics): @@ -6940,6 +9226,102 @@ async def update_model_version_async( headers = {} return await self.update_model_version_with_options_async(model_id, version_name, request, headers, runtime) + def update_service_template_with_options( + self, + service_template_id: str, + request: aiwork_space_20210204_models.UpdateServiceTemplateRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.UpdateServiceTemplateResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.inference_spec): + body['InferenceSpec'] = request.inference_spec + if not UtilClient.is_unset(request.order_number): + body['OrderNumber'] = request.order_number + if not UtilClient.is_unset(request.service_template_description): + body['ServiceTemplateDescription'] = request.service_template_description + if not UtilClient.is_unset(request.service_template_doc): + body['ServiceTemplateDoc'] = request.service_template_doc + if not UtilClient.is_unset(request.service_template_name): + body['ServiceTemplateName'] = request.service_template_name + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='UpdateServiceTemplate', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/servicetemplates/{OpenApiUtilClient.get_encode_param(service_template_id)}', + method='PUT', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.UpdateServiceTemplateResponse(), + self.call_api(params, req, runtime) + ) + + async def update_service_template_with_options_async( + self, + service_template_id: str, + request: aiwork_space_20210204_models.UpdateServiceTemplateRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> aiwork_space_20210204_models.UpdateServiceTemplateResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.inference_spec): + body['InferenceSpec'] = request.inference_spec + if not UtilClient.is_unset(request.order_number): + body['OrderNumber'] = request.order_number + if not UtilClient.is_unset(request.service_template_description): + body['ServiceTemplateDescription'] = request.service_template_description + if not UtilClient.is_unset(request.service_template_doc): + body['ServiceTemplateDoc'] = request.service_template_doc + if not UtilClient.is_unset(request.service_template_name): + body['ServiceTemplateName'] = request.service_template_name + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='UpdateServiceTemplate', + version='2021-02-04', + protocol='HTTPS', + pathname=f'/api/v1/servicetemplates/{OpenApiUtilClient.get_encode_param(service_template_id)}', + method='PUT', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + aiwork_space_20210204_models.UpdateServiceTemplateResponse(), + await self.call_api_async(params, req, runtime) + ) + + def update_service_template( + self, + service_template_id: str, + request: aiwork_space_20210204_models.UpdateServiceTemplateRequest, + ) -> aiwork_space_20210204_models.UpdateServiceTemplateResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.update_service_template_with_options(service_template_id, request, headers, runtime) + + async def update_service_template_async( + self, + service_template_id: str, + request: aiwork_space_20210204_models.UpdateServiceTemplateRequest, + ) -> aiwork_space_20210204_models.UpdateServiceTemplateResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.update_service_template_with_options_async(service_template_id, request, headers, runtime) + def update_workspace_with_options( self, workspace_id: str, @@ -7037,10 +9419,16 @@ def update_workspace_resource_with_options( body['GroupName'] = request.group_name if not UtilClient.is_unset(request.is_default): body['IsDefault'] = request.is_default + if not UtilClient.is_unset(request.labels): + body['Labels'] = request.labels if not UtilClient.is_unset(request.product_type): body['ProductType'] = request.product_type + if not UtilClient.is_unset(request.resource_ids): + body['ResourceIds'] = request.resource_ids if not UtilClient.is_unset(request.resource_type): body['ResourceType'] = request.resource_type + if not UtilClient.is_unset(request.spec): + body['Spec'] = request.spec req = open_api_models.OpenApiRequest( headers=headers, body=OpenApiUtilClient.parse_to_map(body) @@ -7074,10 +9462,16 @@ async def update_workspace_resource_with_options_async( body['GroupName'] = request.group_name if not UtilClient.is_unset(request.is_default): body['IsDefault'] = request.is_default + if not UtilClient.is_unset(request.labels): + body['Labels'] = request.labels if not UtilClient.is_unset(request.product_type): body['ProductType'] = request.product_type + if not UtilClient.is_unset(request.resource_ids): + body['ResourceIds'] = request.resource_ids if not UtilClient.is_unset(request.resource_type): body['ResourceType'] = request.resource_type + if not UtilClient.is_unset(request.spec): + body['Spec'] = request.spec req = open_api_models.OpenApiRequest( headers=headers, body=OpenApiUtilClient.parse_to_map(body) diff --git a/pai/libs/alibabacloud_aiworkspace20210204/models.py b/pai/libs/alibabacloud_aiworkspace20210204/models.py index 744f94c..b3d63aa 100644 --- a/pai/libs/alibabacloud_aiworkspace20210204/models.py +++ b/pai/libs/alibabacloud_aiworkspace20210204/models.py @@ -109,6 +109,57 @@ def from_map(self, m: dict = None): return self +class Collection(TeaModel): + def __init__( + self, + collection_name: str = None, + gmt_create_time: str = None, + gmt_modified_time: str = None, + owner_id: str = None, + user_id: str = None, + ): + self.collection_name = collection_name + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time + self.owner_id = owner_id + self.user_id = user_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.collection_name is not None: + result['CollectionName'] = self.collection_name + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + if self.owner_id is not None: + result['OwnerId'] = self.owner_id + if self.user_id is not None: + result['UserId'] = self.user_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('CollectionName') is not None: + self.collection_name = m.get('CollectionName') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + if m.get('OwnerId') is not None: + self.owner_id = m.get('OwnerId') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + return self + + class Label(TeaModel): def __init__( self, @@ -312,10 +363,177 @@ def from_map(self, m: dict = None): return self +class Experiment(TeaModel): + def __init__( + self, + artifact_uri: str = None, + experiment_id: str = None, + gmt_create_time: str = None, + gmt_modified_time: str = None, + labels: List[Dict[str, Any]] = None, + name: str = None, + owner_id: str = None, + tensorboard_log_uri: str = None, + user_id: str = None, + workspace_id: str = None, + ): + self.artifact_uri = artifact_uri + self.experiment_id = experiment_id + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time + self.labels = labels + self.name = name + self.owner_id = owner_id + self.tensorboard_log_uri = tensorboard_log_uri + self.user_id = user_id + self.workspace_id = workspace_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.artifact_uri is not None: + result['ArtifactUri'] = self.artifact_uri + if self.experiment_id is not None: + result['ExperimentId'] = self.experiment_id + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + if self.labels is not None: + result['Labels'] = self.labels + if self.name is not None: + result['Name'] = self.name + if self.owner_id is not None: + result['OwnerId'] = self.owner_id + if self.tensorboard_log_uri is not None: + result['TensorboardLogUri'] = self.tensorboard_log_uri + if self.user_id is not None: + result['UserId'] = self.user_id + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('ArtifactUri') is not None: + self.artifact_uri = m.get('ArtifactUri') + if m.get('ExperimentId') is not None: + self.experiment_id = m.get('ExperimentId') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + if m.get('Labels') is not None: + self.labels = m.get('Labels') + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('OwnerId') is not None: + self.owner_id = m.get('OwnerId') + if m.get('TensorboardLogUri') is not None: + self.tensorboard_log_uri = m.get('TensorboardLogUri') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') + return self + + +class ExperimentLabel(TeaModel): + def __init__( + self, + experiment_id: str = None, + gmt_create_time: str = None, + gmt_modified_time: str = None, + key: str = None, + value: str = None, + ): + self.experiment_id = experiment_id + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time + self.key = key + self.value = value + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.experiment_id is not None: + result['ExperimentId'] = self.experiment_id + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + if self.key is not None: + result['Key'] = self.key + if self.value is not None: + result['Value'] = self.value + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('ExperimentId') is not None: + self.experiment_id = m.get('ExperimentId') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + if m.get('Key') is not None: + self.key = m.get('Key') + if m.get('Value') is not None: + self.value = m.get('Value') + return self + + +class LabelInfo(TeaModel): + def __init__( + self, + key: str = None, + value: str = None, + ): + self.key = key + self.value = value + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.key is not None: + result['Key'] = self.key + if self.value is not None: + result['Value'] = self.value + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Key') is not None: + self.key = m.get('Key') + if m.get('Value') is not None: + self.value = m.get('Value') + return self + + class ModelVersion(TeaModel): def __init__( self, approval_status: str = None, + evaluation_spec: Dict[str, Any] = None, + extra_info: Dict[str, Any] = None, format_type: str = None, framework_type: str = None, gmt_create_time: str = None, @@ -334,6 +552,8 @@ def __init__( version_name: str = None, ): self.approval_status = approval_status + self.evaluation_spec = evaluation_spec + self.extra_info = extra_info self.format_type = format_type self.framework_type = framework_type self.gmt_create_time = gmt_create_time @@ -365,6 +585,10 @@ def to_map(self): result = dict() if self.approval_status is not None: result['ApprovalStatus'] = self.approval_status + if self.evaluation_spec is not None: + result['EvaluationSpec'] = self.evaluation_spec + if self.extra_info is not None: + result['ExtraInfo'] = self.extra_info if self.format_type is not None: result['FormatType'] = self.format_type if self.framework_type is not None: @@ -405,6 +629,10 @@ def from_map(self, m: dict = None): m = m or dict() if m.get('ApprovalStatus') is not None: self.approval_status = m.get('ApprovalStatus') + if m.get('EvaluationSpec') is not None: + self.evaluation_spec = m.get('EvaluationSpec') + if m.get('ExtraInfo') is not None: + self.extra_info = m.get('ExtraInfo') if m.get('FormatType') is not None: self.format_type = m.get('FormatType') if m.get('FrameworkType') is not None: @@ -448,6 +676,7 @@ def __init__( self, accessibility: str = None, domain: str = None, + extra_info: Dict[str, Any] = None, gmt_create_time: str = None, gmt_modified_time: str = None, labels: List[Label] = None, @@ -456,6 +685,8 @@ def __init__( model_doc: str = None, model_id: str = None, model_name: str = None, + model_type: str = None, + order_number: int = None, origin: str = None, owner_id: str = None, provider: str = None, @@ -465,6 +696,7 @@ def __init__( ): self.accessibility = accessibility self.domain = domain + self.extra_info = extra_info self.gmt_create_time = gmt_create_time self.gmt_modified_time = gmt_modified_time self.labels = labels @@ -473,6 +705,8 @@ def __init__( self.model_doc = model_doc self.model_id = model_id self.model_name = model_name + self.model_type = model_type + self.order_number = order_number self.origin = origin self.owner_id = owner_id self.provider = provider @@ -498,6 +732,8 @@ def to_map(self): result['Accessibility'] = self.accessibility if self.domain is not None: result['Domain'] = self.domain + if self.extra_info is not None: + result['ExtraInfo'] = self.extra_info if self.gmt_create_time is not None: result['GmtCreateTime'] = self.gmt_create_time if self.gmt_modified_time is not None: @@ -516,6 +752,10 @@ def to_map(self): result['ModelId'] = self.model_id if self.model_name is not None: result['ModelName'] = self.model_name + if self.model_type is not None: + result['ModelType'] = self.model_type + if self.order_number is not None: + result['OrderNumber'] = self.order_number if self.origin is not None: result['Origin'] = self.origin if self.owner_id is not None: @@ -536,6 +776,8 @@ def from_map(self, m: dict = None): self.accessibility = m.get('Accessibility') if m.get('Domain') is not None: self.domain = m.get('Domain') + if m.get('ExtraInfo') is not None: + self.extra_info = m.get('ExtraInfo') if m.get('GmtCreateTime') is not None: self.gmt_create_time = m.get('GmtCreateTime') if m.get('GmtModifiedTime') is not None: @@ -556,6 +798,10 @@ def from_map(self, m: dict = None): self.model_id = m.get('ModelId') if m.get('ModelName') is not None: self.model_name = m.get('ModelName') + if m.get('ModelType') is not None: + self.model_type = m.get('ModelType') + if m.get('OrderNumber') is not None: + self.order_number = m.get('OrderNumber') if m.get('Origin') is not None: self.origin = m.get('Origin') if m.get('OwnerId') is not None: @@ -571,15 +817,40 @@ def from_map(self, m: dict = None): return self -class ResourcesExecutorValue(TeaModel): +class ServiceTemplate(TeaModel): def __init__( self, + gmt_create_time: str = None, + gmt_modified_time: str = None, + inference_spec: Dict[str, Any] = None, + labels: List[Label] = None, + order_number: int = None, owner_id: str = None, + provider: str = None, + service_template_description: str = None, + service_template_doc: str = None, + service_template_id: str = None, + service_template_name: str = None, + user_id: str = None, ): + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time + self.inference_spec = inference_spec + self.labels = labels + self.order_number = order_number self.owner_id = owner_id + self.provider = provider + self.service_template_description = service_template_description + self.service_template_doc = service_template_doc + self.service_template_id = service_template_id + self.service_template_name = service_template_name + self.user_id = user_id def validate(self): - pass + if self.labels: + for k in self.labels: + if k: + k.validate() def to_map(self): _map = super().to_map() @@ -587,28 +858,221 @@ def to_map(self): return _map result = dict() + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + if self.inference_spec is not None: + result['InferenceSpec'] = self.inference_spec + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + if self.order_number is not None: + result['OrderNumber'] = self.order_number if self.owner_id is not None: result['OwnerId'] = self.owner_id + if self.provider is not None: + result['Provider'] = self.provider + if self.service_template_description is not None: + result['ServiceTemplateDescription'] = self.service_template_description + if self.service_template_doc is not None: + result['ServiceTemplateDoc'] = self.service_template_doc + if self.service_template_id is not None: + result['ServiceTemplateId'] = self.service_template_id + if self.service_template_name is not None: + result['ServiceTemplateName'] = self.service_template_name + if self.user_id is not None: + result['UserId'] = self.user_id return result def from_map(self, m: dict = None): m = m or dict() + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + if m.get('InferenceSpec') is not None: + self.inference_spec = m.get('InferenceSpec') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = Label() + self.labels.append(temp_model.from_map(k)) + if m.get('OrderNumber') is not None: + self.order_number = m.get('OrderNumber') if m.get('OwnerId') is not None: self.owner_id = m.get('OwnerId') + if m.get('Provider') is not None: + self.provider = m.get('Provider') + if m.get('ServiceTemplateDescription') is not None: + self.service_template_description = m.get('ServiceTemplateDescription') + if m.get('ServiceTemplateDoc') is not None: + self.service_template_doc = m.get('ServiceTemplateDoc') + if m.get('ServiceTemplateId') is not None: + self.service_template_id = m.get('ServiceTemplateId') + if m.get('ServiceTemplateName') is not None: + self.service_template_name = m.get('ServiceTemplateName') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') return self -class AddImageRequestLabels(TeaModel): +class Trial(TeaModel): def __init__( self, - key: str = None, - value: str = None, + accessibility: str = None, + experiment_id: str = None, + gmt_create_time: str = None, + gmt_modified_time: str = None, + labels: List[Dict[str, Any]] = None, + name: str = None, + owner_id: str = None, + source_id: str = None, + source_type: str = None, + trial_id: str = None, + user_id: str = None, + workspace_id: str = None, ): - self.key = key - self.value = value - - def validate(self): - pass + self.accessibility = accessibility + self.experiment_id = experiment_id + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time + self.labels = labels + self.name = name + self.owner_id = owner_id + self.source_id = source_id + self.source_type = source_type + self.trial_id = trial_id + self.user_id = user_id + self.workspace_id = workspace_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.accessibility is not None: + result['Accessibility'] = self.accessibility + if self.experiment_id is not None: + result['ExperimentId'] = self.experiment_id + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + if self.labels is not None: + result['Labels'] = self.labels + if self.name is not None: + result['Name'] = self.name + if self.owner_id is not None: + result['OwnerId'] = self.owner_id + if self.source_id is not None: + result['SourceId'] = self.source_id + if self.source_type is not None: + result['SourceType'] = self.source_type + if self.trial_id is not None: + result['TrialId'] = self.trial_id + if self.user_id is not None: + result['UserId'] = self.user_id + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Accessibility') is not None: + self.accessibility = m.get('Accessibility') + if m.get('ExperimentId') is not None: + self.experiment_id = m.get('ExperimentId') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + if m.get('Labels') is not None: + self.labels = m.get('Labels') + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('OwnerId') is not None: + self.owner_id = m.get('OwnerId') + if m.get('SourceId') is not None: + self.source_id = m.get('SourceId') + if m.get('SourceType') is not None: + self.source_type = m.get('SourceType') + if m.get('TrialId') is not None: + self.trial_id = m.get('TrialId') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') + return self + + +class TrialLabel(TeaModel): + def __init__( + self, + gmt_create_time: str = None, + gmt_modified_time: str = None, + key: str = None, + trial_id: str = None, + value: str = None, + ): + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time + self.key = key + self.trial_id = trial_id + self.value = value + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + if self.key is not None: + result['Key'] = self.key + if self.trial_id is not None: + result['TrialId'] = self.trial_id + if self.value is not None: + result['Value'] = self.value + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + if m.get('Key') is not None: + self.key = m.get('Key') + if m.get('TrialId') is not None: + self.trial_id = m.get('TrialId') + if m.get('Value') is not None: + self.value = m.get('Value') + return self + + +class AddImageRequestLabels(TeaModel): + def __init__( + self, + key: str = None, + value: str = None, + ): + self.key = key + self.value = value + + def validate(self): + pass def to_map(self): _map = super().to_map() @@ -636,16 +1100,20 @@ def __init__( self, accessibility: str = None, description: str = None, + image_id: str = None, image_uri: str = None, labels: List[AddImageRequestLabels] = None, name: str = None, + size: int = None, workspace_id: str = None, ): self.accessibility = accessibility self.description = description + self.image_id = image_id self.image_uri = image_uri self.labels = labels self.name = name + self.size = size self.workspace_id = workspace_id def validate(self): @@ -664,6 +1132,8 @@ def to_map(self): result['Accessibility'] = self.accessibility if self.description is not None: result['Description'] = self.description + if self.image_id is not None: + result['ImageId'] = self.image_id if self.image_uri is not None: result['ImageUri'] = self.image_uri result['Labels'] = [] @@ -672,6 +1142,8 @@ def to_map(self): result['Labels'].append(k.to_map() if k else None) if self.name is not None: result['Name'] = self.name + if self.size is not None: + result['Size'] = self.size if self.workspace_id is not None: result['WorkspaceId'] = self.workspace_id return result @@ -682,6 +1154,8 @@ def from_map(self, m: dict = None): self.accessibility = m.get('Accessibility') if m.get('Description') is not None: self.description = m.get('Description') + if m.get('ImageId') is not None: + self.image_id = m.get('ImageId') if m.get('ImageUri') is not None: self.image_uri = m.get('ImageUri') self.labels = [] @@ -691,6 +1165,8 @@ def from_map(self, m: dict = None): self.labels.append(temp_model.from_map(k)) if m.get('Name') is not None: self.name = m.get('Name') + if m.get('Size') is not None: + self.size = m.get('Size') if m.get('WorkspaceId') is not None: self.workspace_id = m.get('WorkspaceId') return self @@ -741,9 +1217,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -880,9 +1353,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -951,9 +1421,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -1022,9 +1489,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -1111,9 +1575,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -1263,9 +1724,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -1295,6 +1753,107 @@ def from_map(self, m: dict = None): return self +class CreateCollectionRequest(TeaModel): + def __init__( + self, + collection_name: str = None, + ): + self.collection_name = collection_name + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.collection_name is not None: + result['CollectionName'] = self.collection_name + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('CollectionName') is not None: + self.collection_name = m.get('CollectionName') + return self + + +class CreateCollectionResponseBody(TeaModel): + def __init__( + self, + collection_name: str = None, + request_id: str = None, + ): + self.collection_name = collection_name + self.request_id = request_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.collection_name is not None: + result['CollectionName'] = self.collection_name + if self.request_id is not None: + result['RequestId'] = self.request_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('CollectionName') is not None: + self.collection_name = m.get('CollectionName') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + return self + + +class CreateCollectionResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: CreateCollectionResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = CreateCollectionResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + class CreateDatasetRequest(TeaModel): def __init__( self, @@ -1306,6 +1865,7 @@ def __init__( name: str = None, options: str = None, property: str = None, + provider: str = None, provider_type: str = None, source_id: str = None, source_type: str = None, @@ -1320,6 +1880,7 @@ def __init__( self.name = name self.options = options self.property = property + self.provider = provider self.provider_type = provider_type self.source_id = source_id self.source_type = source_type @@ -1356,6 +1917,8 @@ def to_map(self): result['Options'] = self.options if self.property is not None: result['Property'] = self.property + if self.provider is not None: + result['Provider'] = self.provider if self.provider_type is not None: result['ProviderType'] = self.provider_type if self.source_id is not None: @@ -1389,6 +1952,8 @@ def from_map(self, m: dict = None): self.options = m.get('Options') if m.get('Property') is not None: self.property = m.get('Property') + if m.get('Provider') is not None: + self.provider = m.get('Provider') if m.get('ProviderType') is not None: self.provider_type = m.get('ProviderType') if m.get('SourceId') is not None: @@ -1447,9 +2012,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -1553,9 +2115,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -1716,9 +2275,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -1826,9 +2382,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -1858,17 +2411,30 @@ def from_map(self, m: dict = None): return self -class CreateMemberRequestMembers(TeaModel): +class CreateExperimentRequest(TeaModel): def __init__( self, - roles: List[str] = None, - user_id: str = None, + accessibility: str = None, + artifact_uri: str = None, + labels: List[LabelInfo] = None, + name: str = None, + workspace_id: str = None, ): - self.roles = roles - self.user_id = user_id + self.accessibility = accessibility + # Artifact的OSS存储路径 + self.artifact_uri = artifact_uri + # 标签 + self.labels = labels + # 名称 + self.name = name + # 工作空间ID + self.workspace_id = workspace_id def validate(self): - pass + if self.labels: + for k in self.labels: + if k: + k.validate() def to_map(self): _map = super().to_map() @@ -1876,21 +2442,145 @@ def to_map(self): return _map result = dict() - if self.roles is not None: - result['Roles'] = self.roles - if self.user_id is not None: - result['UserId'] = self.user_id + if self.accessibility is not None: + result['Accessibility'] = self.accessibility + if self.artifact_uri is not None: + result['ArtifactUri'] = self.artifact_uri + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + if self.name is not None: + result['Name'] = self.name + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Roles') is not None: - self.roles = m.get('Roles') - if m.get('UserId') is not None: - self.user_id = m.get('UserId') - return self - - + if m.get('Accessibility') is not None: + self.accessibility = m.get('Accessibility') + if m.get('ArtifactUri') is not None: + self.artifact_uri = m.get('ArtifactUri') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = LabelInfo() + self.labels.append(temp_model.from_map(k)) + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') + return self + + +class CreateExperimentResponseBody(TeaModel): + def __init__( + self, + experiment_id: str = None, + request_id: str = None, + ): + self.experiment_id = experiment_id + self.request_id = request_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.experiment_id is not None: + result['ExperimentId'] = self.experiment_id + if self.request_id is not None: + result['RequestId'] = self.request_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('ExperimentId') is not None: + self.experiment_id = m.get('ExperimentId') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + return self + + +class CreateExperimentResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: CreateExperimentResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = CreateExperimentResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class CreateMemberRequestMembers(TeaModel): + def __init__( + self, + roles: List[str] = None, + user_id: str = None, + ): + self.roles = roles + self.user_id = user_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.roles is not None: + result['Roles'] = self.roles + if self.user_id is not None: + result['UserId'] = self.user_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Roles') is not None: + self.roles = m.get('Roles') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + return self + + class CreateMemberRequest(TeaModel): def __init__( self, @@ -2024,9 +2714,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -2061,20 +2748,26 @@ def __init__( self, accessibility: str = None, domain: str = None, + extra_info: Dict[str, Any] = None, labels: List[Label] = None, model_description: str = None, model_doc: str = None, model_name: str = None, + model_type: str = None, + order_number: int = None, origin: str = None, task: str = None, workspace_id: str = None, ): self.accessibility = accessibility self.domain = domain + self.extra_info = extra_info self.labels = labels self.model_description = model_description self.model_doc = model_doc self.model_name = model_name + self.model_type = model_type + self.order_number = order_number self.origin = origin self.task = task self.workspace_id = workspace_id @@ -2095,6 +2788,8 @@ def to_map(self): result['Accessibility'] = self.accessibility if self.domain is not None: result['Domain'] = self.domain + if self.extra_info is not None: + result['ExtraInfo'] = self.extra_info result['Labels'] = [] if self.labels is not None: for k in self.labels: @@ -2105,6 +2800,10 @@ def to_map(self): result['ModelDoc'] = self.model_doc if self.model_name is not None: result['ModelName'] = self.model_name + if self.model_type is not None: + result['ModelType'] = self.model_type + if self.order_number is not None: + result['OrderNumber'] = self.order_number if self.origin is not None: result['Origin'] = self.origin if self.task is not None: @@ -2119,6 +2818,8 @@ def from_map(self, m: dict = None): self.accessibility = m.get('Accessibility') if m.get('Domain') is not None: self.domain = m.get('Domain') + if m.get('ExtraInfo') is not None: + self.extra_info = m.get('ExtraInfo') self.labels = [] if m.get('Labels') is not None: for k in m.get('Labels'): @@ -2130,6 +2831,10 @@ def from_map(self, m: dict = None): self.model_doc = m.get('ModelDoc') if m.get('ModelName') is not None: self.model_name = m.get('ModelName') + if m.get('ModelType') is not None: + self.model_type = m.get('ModelType') + if m.get('OrderNumber') is not None: + self.order_number = m.get('OrderNumber') if m.get('Origin') is not None: self.origin = m.get('Origin') if m.get('Task') is not None: @@ -2184,9 +2889,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -2290,9 +2992,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -2325,9 +3024,11 @@ def from_map(self, m: dict = None): class CreateModelReleaseRequest(TeaModel): def __init__( self, + collections: str = None, target_model_origin: str = None, target_model_provider: str = None, ): + self.collections = collections self.target_model_origin = target_model_origin self.target_model_provider = target_model_provider @@ -2340,6 +3041,8 @@ def to_map(self): return _map result = dict() + if self.collections is not None: + result['Collections'] = self.collections if self.target_model_origin is not None: result['TargetModelOrigin'] = self.target_model_origin if self.target_model_provider is not None: @@ -2348,6 +3051,8 @@ def to_map(self): def from_map(self, m: dict = None): m = m or dict() + if m.get('Collections') is not None: + self.collections = m.get('Collections') if m.get('TargetModelOrigin') is not None: self.target_model_origin = m.get('TargetModelOrigin') if m.get('TargetModelProvider') is not None: @@ -2400,9 +3105,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -2436,6 +3138,8 @@ class CreateModelVersionRequest(TeaModel): def __init__( self, approval_status: str = None, + evaluation_spec: Dict[str, Any] = None, + extra_info: Dict[str, Any] = None, format_type: str = None, framework_type: str = None, inference_spec: Dict[str, Any] = None, @@ -2450,6 +3154,8 @@ def __init__( version_name: str = None, ): self.approval_status = approval_status + self.evaluation_spec = evaluation_spec + self.extra_info = extra_info self.format_type = format_type self.framework_type = framework_type self.inference_spec = inference_spec @@ -2477,6 +3183,10 @@ def to_map(self): result = dict() if self.approval_status is not None: result['ApprovalStatus'] = self.approval_status + if self.evaluation_spec is not None: + result['EvaluationSpec'] = self.evaluation_spec + if self.extra_info is not None: + result['ExtraInfo'] = self.extra_info if self.format_type is not None: result['FormatType'] = self.format_type if self.framework_type is not None: @@ -2509,6 +3219,10 @@ def from_map(self, m: dict = None): m = m or dict() if m.get('ApprovalStatus') is not None: self.approval_status = m.get('ApprovalStatus') + if m.get('EvaluationSpec') is not None: + self.evaluation_spec = m.get('EvaluationSpec') + if m.get('ExtraInfo') is not None: + self.extra_info = m.get('ExtraInfo') if m.get('FormatType') is not None: self.format_type = m.get('FormatType') if m.get('FrameworkType') is not None: @@ -2584,9 +3298,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -2690,9 +3401,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -2806,9 +3514,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -2952,14 +3657,16 @@ class CreateProductOrdersRequest(TeaModel): def __init__( self, auto_pay: bool = None, - products: CreateProductOrdersRequestProducts = None, + products: List[CreateProductOrdersRequestProducts] = None, ): self.auto_pay = auto_pay self.products = products def validate(self): if self.products: - self.products.validate() + for k in self.products: + if k: + k.validate() def to_map(self): _map = super().to_map() @@ -2969,17 +3676,21 @@ def to_map(self): result = dict() if self.auto_pay is not None: result['AutoPay'] = self.auto_pay + result['Products'] = [] if self.products is not None: - result['Products'] = self.products.to_map() + for k in self.products: + result['Products'].append(k.to_map() if k else None) return result def from_map(self, m: dict = None): m = m or dict() if m.get('AutoPay') is not None: self.auto_pay = m.get('AutoPay') + self.products = [] if m.get('Products') is not None: - temp_model = CreateProductOrdersRequestProducts() - self.products = temp_model.from_map(m['Products']) + for k in m.get('Products'): + temp_model = CreateProductOrdersRequestProducts() + self.products.append(temp_model.from_map(k)) return self @@ -3040,9 +3751,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -3138,9 +3846,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -3170,14 +3875,85 @@ def from_map(self, m: dict = None): return self -class CreateUserResponseBody(TeaModel): +class CreateServiceTemplateRequest(TeaModel): + def __init__( + self, + inference_spec: Dict[str, Any] = None, + labels: List[Label] = None, + order_number: int = None, + provider: str = None, + service_template_description: str = None, + service_template_doc: str = None, + service_template_name: str = None, + ): + self.inference_spec = inference_spec + self.labels = labels + self.order_number = order_number + self.provider = provider + self.service_template_description = service_template_description + self.service_template_doc = service_template_doc + self.service_template_name = service_template_name + + def validate(self): + if self.labels: + for k in self.labels: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.inference_spec is not None: + result['InferenceSpec'] = self.inference_spec + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + if self.order_number is not None: + result['OrderNumber'] = self.order_number + if self.provider is not None: + result['Provider'] = self.provider + if self.service_template_description is not None: + result['ServiceTemplateDescription'] = self.service_template_description + if self.service_template_doc is not None: + result['ServiceTemplateDoc'] = self.service_template_doc + if self.service_template_name is not None: + result['ServiceTemplateName'] = self.service_template_name + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('InferenceSpec') is not None: + self.inference_spec = m.get('InferenceSpec') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = Label() + self.labels.append(temp_model.from_map(k)) + if m.get('OrderNumber') is not None: + self.order_number = m.get('OrderNumber') + if m.get('Provider') is not None: + self.provider = m.get('Provider') + if m.get('ServiceTemplateDescription') is not None: + self.service_template_description = m.get('ServiceTemplateDescription') + if m.get('ServiceTemplateDoc') is not None: + self.service_template_doc = m.get('ServiceTemplateDoc') + if m.get('ServiceTemplateName') is not None: + self.service_template_name = m.get('ServiceTemplateName') + return self + + +class CreateServiceTemplateResponseBody(TeaModel): def __init__( self, request_id: str = None, - user_id: str = None, + service_template_id: str = None, ): self.request_id = request_id - self.user_id = user_id + self.service_template_id = service_template_id def validate(self): pass @@ -3190,34 +3966,31 @@ def to_map(self): result = dict() if self.request_id is not None: result['RequestId'] = self.request_id - if self.user_id is not None: - result['UserId'] = self.user_id + if self.service_template_id is not None: + result['ServiceTemplateId'] = self.service_template_id return result def from_map(self, m: dict = None): m = m or dict() if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('UserId') is not None: - self.user_id = m.get('UserId') + if m.get('ServiceTemplateId') is not None: + self.service_template_id = m.get('ServiceTemplateId') return self -class CreateUserResponse(TeaModel): +class CreateServiceTemplateResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: CreateUserResponseBody = None, + body: CreateServiceTemplateResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -3242,26 +4015,23 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = CreateUserResponseBody() + temp_model = CreateServiceTemplateResponseBody() self.body = temp_model.from_map(m['body']) return self -class CreateWorkspaceRequest(TeaModel): +class CreateServiceTemplateLabelsRequest(TeaModel): def __init__( self, - description: str = None, - display_name: str = None, - env_types: List[str] = None, - workspace_name: str = None, + labels: List[Label] = None, ): - self.description = description - self.display_name = display_name - self.env_types = env_types - self.workspace_name = workspace_name + self.labels = labels def validate(self): - pass + if self.labels: + for k in self.labels: + if k: + k.validate() def to_map(self): _map = super().to_map() @@ -3269,37 +4039,28 @@ def to_map(self): return _map result = dict() - if self.description is not None: - result['Description'] = self.description - if self.display_name is not None: - result['DisplayName'] = self.display_name - if self.env_types is not None: - result['EnvTypes'] = self.env_types - if self.workspace_name is not None: - result['WorkspaceName'] = self.workspace_name + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Description') is not None: - self.description = m.get('Description') - if m.get('DisplayName') is not None: - self.display_name = m.get('DisplayName') - if m.get('EnvTypes') is not None: - self.env_types = m.get('EnvTypes') - if m.get('WorkspaceName') is not None: - self.workspace_name = m.get('WorkspaceName') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = Label() + self.labels.append(temp_model.from_map(k)) return self -class CreateWorkspaceResponseBody(TeaModel): +class CreateServiceTemplateLabelsResponseBody(TeaModel): def __init__( self, request_id: str = None, - workspace_id: str = None, ): self.request_id = request_id - self.workspace_id = workspace_id def validate(self): pass @@ -3312,34 +4073,27 @@ def to_map(self): result = dict() if self.request_id is not None: result['RequestId'] = self.request_id - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') return self -class CreateWorkspaceResponse(TeaModel): +class CreateServiceTemplateLabelsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: CreateWorkspaceResponseBody = None, + body: CreateServiceTemplateLabelsResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -3364,20 +4118,31 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = CreateWorkspaceResponseBody() + temp_model = CreateServiceTemplateLabelsResponseBody() self.body = temp_model.from_map(m['body']) return self -class CreateWorkspaceResourceRequestResourcesQuotas(TeaModel): +class CreateTrialRequest(TeaModel): def __init__( self, - id: str = None, + experiment_id: str = None, + labels: List[LabelInfo] = None, + name: str = None, + source_id: str = None, + source_type: str = None, ): - self.id = id + self.experiment_id = experiment_id + self.labels = labels + self.name = name + self.source_id = source_id + self.source_type = source_type def validate(self): - pass + if self.labels: + for k in self.labels: + if k: + k.validate() def to_map(self): _map = super().to_map() @@ -3385,114 +4150,49 @@ def to_map(self): return _map result = dict() - if self.id is not None: - result['Id'] = self.id - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('Id') is not None: - self.id = m.get('Id') - return self - - -class CreateWorkspaceResourceRequestResources(TeaModel): - def __init__( - self, - env_type: str = None, - group_name: str = None, - is_default: bool = None, - name: str = None, - product_type: str = None, - quotas: List[CreateWorkspaceResourceRequestResourcesQuotas] = None, - resource_type: str = None, - spec: Dict[str, Any] = None, - workspace_id: str = None, - ): - self.env_type = env_type - self.group_name = group_name - self.is_default = is_default - self.name = name - self.product_type = product_type - self.quotas = quotas - self.resource_type = resource_type - self.spec = spec - self.workspace_id = workspace_id - - def validate(self): - if self.quotas: - for k in self.quotas: - if k: - k.validate() - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.env_type is not None: - result['EnvType'] = self.env_type - if self.group_name is not None: - result['GroupName'] = self.group_name - if self.is_default is not None: - result['IsDefault'] = self.is_default + if self.experiment_id is not None: + result['ExperimentId'] = self.experiment_id + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) if self.name is not None: result['Name'] = self.name - if self.product_type is not None: - result['ProductType'] = self.product_type - result['Quotas'] = [] - if self.quotas is not None: - for k in self.quotas: - result['Quotas'].append(k.to_map() if k else None) - if self.resource_type is not None: - result['ResourceType'] = self.resource_type - if self.spec is not None: - result['Spec'] = self.spec - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id + if self.source_id is not None: + result['SourceId'] = self.source_id + if self.source_type is not None: + result['SourceType'] = self.source_type return result def from_map(self, m: dict = None): m = m or dict() - if m.get('EnvType') is not None: - self.env_type = m.get('EnvType') - if m.get('GroupName') is not None: - self.group_name = m.get('GroupName') - if m.get('IsDefault') is not None: - self.is_default = m.get('IsDefault') + if m.get('ExperimentId') is not None: + self.experiment_id = m.get('ExperimentId') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = LabelInfo() + self.labels.append(temp_model.from_map(k)) if m.get('Name') is not None: self.name = m.get('Name') - if m.get('ProductType') is not None: - self.product_type = m.get('ProductType') - self.quotas = [] - if m.get('Quotas') is not None: - for k in m.get('Quotas'): - temp_model = CreateWorkspaceResourceRequestResourcesQuotas() - self.quotas.append(temp_model.from_map(k)) - if m.get('ResourceType') is not None: - self.resource_type = m.get('ResourceType') - if m.get('Spec') is not None: - self.spec = m.get('Spec') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') + if m.get('SourceId') is not None: + self.source_id = m.get('SourceId') + if m.get('SourceType') is not None: + self.source_type = m.get('SourceType') return self -class CreateWorkspaceResourceRequest(TeaModel): +class CreateTrialResponseBody(TeaModel): def __init__( self, - option: str = None, - resources: List[CreateWorkspaceResourceRequestResources] = None, + request_id: str = None, + trial_id: str = None, ): - self.option = option - self.resources = resources + self.request_id = request_id + self.trial_id = trial_id def validate(self): - if self.resources: - for k in self.resources: - if k: - k.validate() + pass def to_map(self): _map = super().to_map() @@ -3500,35 +4200,35 @@ def to_map(self): return _map result = dict() - if self.option is not None: - result['Option'] = self.option - result['Resources'] = [] - if self.resources is not None: - for k in self.resources: - result['Resources'].append(k.to_map() if k else None) + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.trial_id is not None: + result['TrialId'] = self.trial_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Option') is not None: - self.option = m.get('Option') - self.resources = [] - if m.get('Resources') is not None: - for k in m.get('Resources'): - temp_model = CreateWorkspaceResourceRequestResources() - self.resources.append(temp_model.from_map(k)) + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('TrialId') is not None: + self.trial_id = m.get('TrialId') return self -class CreateWorkspaceResourceResponseBodyResources(TeaModel): +class CreateTrialResponse(TeaModel): def __init__( self, - id: str = None, + headers: Dict[str, str] = None, + status_code: int = None, + body: CreateTrialResponseBody = None, ): - self.id = id + self.headers = headers + self.status_code = status_code + self.body = body def validate(self): - pass + if self.body: + self.body.validate() def to_map(self): _map = super().to_map() @@ -3536,33 +4236,37 @@ def to_map(self): return _map result = dict() - if self.id is not None: - result['Id'] = self.id + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Id') is not None: - self.id = m.get('Id') + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = CreateTrialResponseBody() + self.body = temp_model.from_map(m['body']) return self -class CreateWorkspaceResourceResponseBody(TeaModel): +class CreateUserResponseBody(TeaModel): def __init__( self, request_id: str = None, - resources: List[CreateWorkspaceResourceResponseBodyResources] = None, - total_count: int = None, + user_id: str = None, ): self.request_id = request_id - self.resources = resources - self.total_count = total_count + self.user_id = user_id def validate(self): - if self.resources: - for k in self.resources: - if k: - k.validate() + pass def to_map(self): _map = super().to_map() @@ -3572,43 +4276,31 @@ def to_map(self): result = dict() if self.request_id is not None: result['RequestId'] = self.request_id - result['Resources'] = [] - if self.resources is not None: - for k in self.resources: - result['Resources'].append(k.to_map() if k else None) - if self.total_count is not None: - result['TotalCount'] = self.total_count + if self.user_id is not None: + result['UserId'] = self.user_id return result def from_map(self, m: dict = None): m = m or dict() if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - self.resources = [] - if m.get('Resources') is not None: - for k in m.get('Resources'): - temp_model = CreateWorkspaceResourceResponseBodyResources() - self.resources.append(temp_model.from_map(k)) - if m.get('TotalCount') is not None: - self.total_count = m.get('TotalCount') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') return self -class CreateWorkspaceResourceResponse(TeaModel): +class CreateUserResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: CreateWorkspaceResourceResponseBody = None, + body: CreateUserResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -3633,19 +4325,64 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = CreateWorkspaceResourceResponseBody() + temp_model = CreateUserResponseBody() self.body = temp_model.from_map(m['body']) return self -class DeleteCodeSourceResponseBody(TeaModel): +class CreateWorkspaceRequest(TeaModel): + def __init__( + self, + description: str = None, + display_name: str = None, + env_types: List[str] = None, + workspace_name: str = None, + ): + self.description = description + self.display_name = display_name + self.env_types = env_types + self.workspace_name = workspace_name + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.description is not None: + result['Description'] = self.description + if self.display_name is not None: + result['DisplayName'] = self.display_name + if self.env_types is not None: + result['EnvTypes'] = self.env_types + if self.workspace_name is not None: + result['WorkspaceName'] = self.workspace_name + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Description') is not None: + self.description = m.get('Description') + if m.get('DisplayName') is not None: + self.display_name = m.get('DisplayName') + if m.get('EnvTypes') is not None: + self.env_types = m.get('EnvTypes') + if m.get('WorkspaceName') is not None: + self.workspace_name = m.get('WorkspaceName') + return self + + +class CreateWorkspaceResponseBody(TeaModel): def __init__( self, - code_source_id: str = None, request_id: str = None, + workspace_id: str = None, ): - self.code_source_id = code_source_id self.request_id = request_id + self.workspace_id = workspace_id def validate(self): pass @@ -3656,36 +4393,33 @@ def to_map(self): return _map result = dict() - if self.code_source_id is not None: - result['CodeSourceId'] = self.code_source_id if self.request_id is not None: result['RequestId'] = self.request_id + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('CodeSourceId') is not None: - self.code_source_id = m.get('CodeSourceId') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class DeleteCodeSourceResponse(TeaModel): +class CreateWorkspaceResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: DeleteCodeSourceResponseBody = None, + body: CreateWorkspaceResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -3710,17 +4444,19 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = DeleteCodeSourceResponseBody() + temp_model = CreateWorkspaceResponseBody() self.body = temp_model.from_map(m['body']) return self -class DeleteConfigResponseBody(TeaModel): +class CreateWorkspaceResourceRequestResourcesLabels(TeaModel): def __init__( self, - request_id: str = None, - ): - self.request_id = request_id + key: str = None, + value: str = None, + ): + self.key = key + self.value = value def validate(self): pass @@ -3731,34 +4467,30 @@ def to_map(self): return _map result = dict() - if self.request_id is not None: - result['RequestId'] = self.request_id + if self.key is not None: + result['Key'] = self.key + if self.value is not None: + result['Value'] = self.value return result def from_map(self, m: dict = None): m = m or dict() - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') + if m.get('Key') is not None: + self.key = m.get('Key') + if m.get('Value') is not None: + self.value = m.get('Value') return self -class DeleteConfigResponse(TeaModel): +class CreateWorkspaceResourceRequestResourcesQuotas(TeaModel): def __init__( self, - headers: Dict[str, str] = None, - status_code: int = None, - body: DeleteConfigResponseBody = None, + id: str = None, ): - self.headers = headers - self.status_code = status_code - self.body = body + self.id = id def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') - if self.body: - self.body.validate() + pass def to_map(self): _map = super().to_map() @@ -3766,35 +4498,51 @@ def to_map(self): return _map result = dict() - if self.headers is not None: - result['headers'] = self.headers - if self.status_code is not None: - result['statusCode'] = self.status_code - if self.body is not None: - result['body'] = self.body.to_map() + if self.id is not None: + result['Id'] = self.id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('headers') is not None: - self.headers = m.get('headers') - if m.get('statusCode') is not None: - self.status_code = m.get('statusCode') - if m.get('body') is not None: - temp_model = DeleteConfigResponseBody() - self.body = temp_model.from_map(m['body']) + if m.get('Id') is not None: + self.id = m.get('Id') return self -class DeleteDatasetResponseBody(TeaModel): +class CreateWorkspaceResourceRequestResources(TeaModel): def __init__( self, - request_id: str = None, + env_type: str = None, + group_name: str = None, + is_default: bool = None, + labels: List[CreateWorkspaceResourceRequestResourcesLabels] = None, + name: str = None, + product_type: str = None, + quotas: List[CreateWorkspaceResourceRequestResourcesQuotas] = None, + resource_type: str = None, + spec: Dict[str, Any] = None, + workspace_id: str = None, ): - self.request_id = request_id + self.env_type = env_type + self.group_name = group_name + self.is_default = is_default + self.labels = labels + self.name = name + self.product_type = product_type + self.quotas = quotas + self.resource_type = resource_type + self.spec = spec + self.workspace_id = workspace_id def validate(self): - pass + if self.labels: + for k in self.labels: + if k: + k.validate() + if self.quotas: + for k in self.quotas: + if k: + k.validate() def to_map(self): _map = super().to_map() @@ -3802,34 +4550,77 @@ def to_map(self): return _map result = dict() - if self.request_id is not None: - result['RequestId'] = self.request_id + if self.env_type is not None: + result['EnvType'] = self.env_type + if self.group_name is not None: + result['GroupName'] = self.group_name + if self.is_default is not None: + result['IsDefault'] = self.is_default + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + if self.name is not None: + result['Name'] = self.name + if self.product_type is not None: + result['ProductType'] = self.product_type + result['Quotas'] = [] + if self.quotas is not None: + for k in self.quotas: + result['Quotas'].append(k.to_map() if k else None) + if self.resource_type is not None: + result['ResourceType'] = self.resource_type + if self.spec is not None: + result['Spec'] = self.spec + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') + if m.get('EnvType') is not None: + self.env_type = m.get('EnvType') + if m.get('GroupName') is not None: + self.group_name = m.get('GroupName') + if m.get('IsDefault') is not None: + self.is_default = m.get('IsDefault') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = CreateWorkspaceResourceRequestResourcesLabels() + self.labels.append(temp_model.from_map(k)) + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('ProductType') is not None: + self.product_type = m.get('ProductType') + self.quotas = [] + if m.get('Quotas') is not None: + for k in m.get('Quotas'): + temp_model = CreateWorkspaceResourceRequestResourcesQuotas() + self.quotas.append(temp_model.from_map(k)) + if m.get('ResourceType') is not None: + self.resource_type = m.get('ResourceType') + if m.get('Spec') is not None: + self.spec = m.get('Spec') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class DeleteDatasetResponse(TeaModel): +class CreateWorkspaceResourceRequest(TeaModel): def __init__( self, - headers: Dict[str, str] = None, - status_code: int = None, - body: DeleteDatasetResponseBody = None, + option: str = None, + resources: List[CreateWorkspaceResourceRequestResources] = None, ): - self.headers = headers - self.status_code = status_code - self.body = body + self.option = option + self.resources = resources def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') - if self.body: - self.body.validate() + if self.resources: + for k in self.resources: + if k: + k.validate() def to_map(self): _map = super().to_map() @@ -3837,34 +4628,32 @@ def to_map(self): return _map result = dict() - if self.headers is not None: - result['headers'] = self.headers - if self.status_code is not None: - result['statusCode'] = self.status_code - if self.body is not None: - result['body'] = self.body.to_map() + if self.option is not None: + result['Option'] = self.option + result['Resources'] = [] + if self.resources is not None: + for k in self.resources: + result['Resources'].append(k.to_map() if k else None) return result def from_map(self, m: dict = None): m = m or dict() - if m.get('headers') is not None: - self.headers = m.get('headers') - if m.get('statusCode') is not None: - self.status_code = m.get('statusCode') - if m.get('body') is not None: - temp_model = DeleteDatasetResponseBody() - self.body = temp_model.from_map(m['body']) + if m.get('Option') is not None: + self.option = m.get('Option') + self.resources = [] + if m.get('Resources') is not None: + for k in m.get('Resources'): + temp_model = CreateWorkspaceResourceRequestResources() + self.resources.append(temp_model.from_map(k)) return self -class DeleteDatasetLabelsRequest(TeaModel): +class CreateWorkspaceResourceResponseBodyResources(TeaModel): def __init__( self, - keys: str = None, - label_keys: str = None, + id: str = None, ): - self.keys = keys - self.label_keys = label_keys + self.id = id def validate(self): pass @@ -3875,30 +4664,33 @@ def to_map(self): return _map result = dict() - if self.keys is not None: - result['Keys'] = self.keys - if self.label_keys is not None: - result['LabelKeys'] = self.label_keys + if self.id is not None: + result['Id'] = self.id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Keys') is not None: - self.keys = m.get('Keys') - if m.get('LabelKeys') is not None: - self.label_keys = m.get('LabelKeys') + if m.get('Id') is not None: + self.id = m.get('Id') return self -class DeleteDatasetLabelsResponseBody(TeaModel): +class CreateWorkspaceResourceResponseBody(TeaModel): def __init__( self, request_id: str = None, + resources: List[CreateWorkspaceResourceResponseBodyResources] = None, + total_count: int = None, ): self.request_id = request_id + self.resources = resources + self.total_count = total_count def validate(self): - pass + if self.resources: + for k in self.resources: + if k: + k.validate() def to_map(self): _map = super().to_map() @@ -3908,30 +4700,40 @@ def to_map(self): result = dict() if self.request_id is not None: result['RequestId'] = self.request_id + result['Resources'] = [] + if self.resources is not None: + for k in self.resources: + result['Resources'].append(k.to_map() if k else None) + if self.total_count is not None: + result['TotalCount'] = self.total_count return result def from_map(self, m: dict = None): m = m or dict() if m.get('RequestId') is not None: self.request_id = m.get('RequestId') + self.resources = [] + if m.get('Resources') is not None: + for k in m.get('Resources'): + temp_model = CreateWorkspaceResourceResponseBodyResources() + self.resources.append(temp_model.from_map(k)) + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') return self -class DeleteDatasetLabelsResponse(TeaModel): +class CreateWorkspaceResourceResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: DeleteDatasetLabelsResponseBody = None, + body: CreateWorkspaceResourceResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -3956,17 +4758,19 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = DeleteDatasetLabelsResponseBody() + temp_model = CreateWorkspaceResourceResponseBody() self.body = temp_model.from_map(m['body']) return self -class DeleteMembersRequest(TeaModel): +class DeleteCodeSourceResponseBody(TeaModel): def __init__( self, - member_ids: str = None, + code_source_id: str = None, + request_id: str = None, ): - self.member_ids = member_ids + self.code_source_id = code_source_id + self.request_id = request_id def validate(self): pass @@ -3977,59 +4781,33 @@ def to_map(self): return _map result = dict() - if self.member_ids is not None: - result['MemberIds'] = self.member_ids + if self.code_source_id is not None: + result['CodeSourceId'] = self.code_source_id + if self.request_id is not None: + result['RequestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('MemberIds') is not None: - self.member_ids = m.get('MemberIds') + if m.get('CodeSourceId') is not None: + self.code_source_id = m.get('CodeSourceId') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') return self -class DeleteMembersResponseBody(TeaModel): +class DeleteCodeSourceResponse(TeaModel): def __init__( self, - request_id: str = None, - ): - self.request_id = request_id - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.request_id is not None: - result['RequestId'] = self.request_id - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - return self - - -class DeleteMembersResponse(TeaModel): - def __init__( - self, - headers: Dict[str, str] = None, - status_code: int = None, - body: DeleteMembersResponseBody = None, + headers: Dict[str, str] = None, + status_code: int = None, + body: DeleteCodeSourceResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -4054,12 +4832,12 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = DeleteMembersResponseBody() + temp_model = DeleteCodeSourceResponseBody() self.body = temp_model.from_map(m['body']) return self -class DeleteModelResponseBody(TeaModel): +class DeleteCollectionResponseBody(TeaModel): def __init__( self, request_id: str = None, @@ -4086,21 +4864,18 @@ def from_map(self, m: dict = None): return self -class DeleteModelResponse(TeaModel): +class DeleteCollectionResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: DeleteModelResponseBody = None, + body: DeleteCollectionResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -4125,17 +4900,17 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = DeleteModelResponseBody() + temp_model = DeleteCollectionResponseBody() self.body = temp_model.from_map(m['body']) return self -class DeleteModelDomainRequest(TeaModel): +class DeleteConfigRequest(TeaModel): def __init__( self, - model_task_ids: str = None, + labels: str = None, ): - self.model_task_ids = model_task_ids + self.labels = labels def validate(self): pass @@ -4146,18 +4921,18 @@ def to_map(self): return _map result = dict() - if self.model_task_ids is not None: - result['ModelTaskIds'] = self.model_task_ids + if self.labels is not None: + result['Labels'] = self.labels return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ModelTaskIds') is not None: - self.model_task_ids = m.get('ModelTaskIds') + if m.get('Labels') is not None: + self.labels = m.get('Labels') return self -class DeleteModelDomainResponseBody(TeaModel): +class DeleteConfigResponseBody(TeaModel): def __init__( self, request_id: str = None, @@ -4184,21 +4959,18 @@ def from_map(self, m: dict = None): return self -class DeleteModelDomainResponse(TeaModel): +class DeleteConfigResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: DeleteModelDomainResponseBody = None, + body: DeleteConfigResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -4223,45 +4995,12 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = DeleteModelDomainResponseBody() + temp_model = DeleteConfigResponseBody() self.body = temp_model.from_map(m['body']) return self -class DeleteModelLabelsRequest(TeaModel): - def __init__( - self, - keys: str = None, - label_keys: str = None, - ): - self.keys = keys - self.label_keys = label_keys - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.keys is not None: - result['Keys'] = self.keys - if self.label_keys is not None: - result['LabelKeys'] = self.label_keys - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('Keys') is not None: - self.keys = m.get('Keys') - if m.get('LabelKeys') is not None: - self.label_keys = m.get('LabelKeys') - return self - - -class DeleteModelLabelsResponseBody(TeaModel): +class DeleteDatasetResponseBody(TeaModel): def __init__( self, request_id: str = None, @@ -4288,21 +5027,18 @@ def from_map(self, m: dict = None): return self -class DeleteModelLabelsResponse(TeaModel): +class DeleteDatasetResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: DeleteModelLabelsResponseBody = None, + body: DeleteDatasetResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -4327,12 +5063,45 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = DeleteModelLabelsResponseBody() + temp_model = DeleteDatasetResponseBody() self.body = temp_model.from_map(m['body']) return self -class DeleteModelVersionResponseBody(TeaModel): +class DeleteDatasetLabelsRequest(TeaModel): + def __init__( + self, + keys: str = None, + label_keys: str = None, + ): + self.keys = keys + self.label_keys = label_keys + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.keys is not None: + result['Keys'] = self.keys + if self.label_keys is not None: + result['LabelKeys'] = self.label_keys + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Keys') is not None: + self.keys = m.get('Keys') + if m.get('LabelKeys') is not None: + self.label_keys = m.get('LabelKeys') + return self + + +class DeleteDatasetLabelsResponseBody(TeaModel): def __init__( self, request_id: str = None, @@ -4359,21 +5128,18 @@ def from_map(self, m: dict = None): return self -class DeleteModelVersionResponse(TeaModel): +class DeleteDatasetLabelsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: DeleteModelVersionResponseBody = None, + body: DeleteDatasetLabelsResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -4398,45 +5164,12 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = DeleteModelVersionResponseBody() + temp_model = DeleteDatasetLabelsResponseBody() self.body = temp_model.from_map(m['body']) return self -class DeleteModelVersionLabelsRequest(TeaModel): - def __init__( - self, - keys: str = None, - label_keys: str = None, - ): - self.keys = keys - self.label_keys = label_keys - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.keys is not None: - result['Keys'] = self.keys - if self.label_keys is not None: - result['LabelKeys'] = self.label_keys - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('Keys') is not None: - self.keys = m.get('Keys') - if m.get('LabelKeys') is not None: - self.label_keys = m.get('LabelKeys') - return self - - -class DeleteModelVersionLabelsResponseBody(TeaModel): +class DeleteExperimentResponseBody(TeaModel): def __init__( self, request_id: str = None, @@ -4463,21 +5196,18 @@ def from_map(self, m: dict = None): return self -class DeleteModelVersionLabelsResponse(TeaModel): +class DeleteExperimentResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: DeleteModelVersionLabelsResponseBody = None, + body: DeleteExperimentResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -4502,12 +5232,12 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = DeleteModelVersionLabelsResponseBody() + temp_model = DeleteExperimentResponseBody() self.body = temp_model.from_map(m['body']) return self -class DeleteWorkspaceResponseBody(TeaModel): +class DeleteExperimentLabelResponseBody(TeaModel): def __init__( self, request_id: str = None, @@ -4534,21 +5264,18 @@ def from_map(self, m: dict = None): return self -class DeleteWorkspaceResponse(TeaModel): +class DeleteExperimentLabelResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: DeleteWorkspaceResponseBody = None, + body: DeleteExperimentLabelResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -4573,23 +5300,17 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = DeleteWorkspaceResponseBody() + temp_model = DeleteExperimentLabelResponseBody() self.body = temp_model.from_map(m['body']) return self -class DeleteWorkspaceResourceRequest(TeaModel): +class DeleteMembersRequest(TeaModel): def __init__( self, - group_name: str = None, - option: str = None, - product_type: str = None, - resource_type: str = None, + member_ids: str = None, ): - self.group_name = group_name - self.option = option - self.product_type = product_type - self.resource_type = resource_type + self.member_ids = member_ids def validate(self): pass @@ -4600,34 +5321,26 @@ def to_map(self): return _map result = dict() - if self.group_name is not None: - result['GroupName'] = self.group_name - if self.option is not None: - result['Option'] = self.option - if self.product_type is not None: - result['ProductType'] = self.product_type - if self.resource_type is not None: - result['ResourceType'] = self.resource_type + if self.member_ids is not None: + result['MemberIds'] = self.member_ids return result def from_map(self, m: dict = None): m = m or dict() - if m.get('GroupName') is not None: - self.group_name = m.get('GroupName') - if m.get('Option') is not None: - self.option = m.get('Option') - if m.get('ProductType') is not None: - self.product_type = m.get('ProductType') - if m.get('ResourceType') is not None: - self.resource_type = m.get('ResourceType') + if m.get('MemberIds') is not None: + self.member_ids = m.get('MemberIds') return self -class DeleteWorkspaceResourceResponseBody(TeaModel): +class DeleteMembersResponseBody(TeaModel): def __init__( self, + code: str = None, + message: str = None, request_id: str = None, ): + self.code = code + self.message = message self.request_id = request_id def validate(self): @@ -4639,32 +5352,37 @@ def to_map(self): return _map result = dict() + if self.code is not None: + result['Code'] = self.code + if self.message is not None: + result['Message'] = self.message if self.request_id is not None: result['RequestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() + if m.get('Code') is not None: + self.code = m.get('Code') + if m.get('Message') is not None: + self.message = m.get('Message') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') return self -class DeleteWorkspaceResourceResponse(TeaModel): +class DeleteMembersResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: DeleteWorkspaceResourceResponseBody = None, + body: DeleteMembersResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -4689,45 +5407,17 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = DeleteWorkspaceResourceResponseBody() + temp_model = DeleteMembersResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetCodeSourceResponseBody(TeaModel): +class DeleteModelResponseBody(TeaModel): def __init__( self, - accessibility: str = None, - code_branch: str = None, - code_commit: str = None, - code_repo: str = None, - code_repo_access_token: str = None, - code_repo_user_name: str = None, - code_source_id: str = None, - description: str = None, - display_name: str = None, - gmt_create_time: str = None, - gmt_modify_time: str = None, - mount_path: str = None, request_id: str = None, - user_id: str = None, - workspace_id: str = None, ): - self.accessibility = accessibility - self.code_branch = code_branch - self.code_commit = code_commit - self.code_repo = code_repo - self.code_repo_access_token = code_repo_access_token - self.code_repo_user_name = code_repo_user_name - self.code_source_id = code_source_id - self.description = description - self.display_name = display_name - self.gmt_create_time = gmt_create_time - self.gmt_modify_time = gmt_modify_time - self.mount_path = mount_path self.request_id = request_id - self.user_id = user_id - self.workspace_id = workspace_id def validate(self): pass @@ -4738,88 +5428,29 @@ def to_map(self): return _map result = dict() - if self.accessibility is not None: - result['Accessibility'] = self.accessibility - if self.code_branch is not None: - result['CodeBranch'] = self.code_branch - if self.code_commit is not None: - result['CodeCommit'] = self.code_commit - if self.code_repo is not None: - result['CodeRepo'] = self.code_repo - if self.code_repo_access_token is not None: - result['CodeRepoAccessToken'] = self.code_repo_access_token - if self.code_repo_user_name is not None: - result['CodeRepoUserName'] = self.code_repo_user_name - if self.code_source_id is not None: - result['CodeSourceId'] = self.code_source_id - if self.description is not None: - result['Description'] = self.description - if self.display_name is not None: - result['DisplayName'] = self.display_name - if self.gmt_create_time is not None: - result['GmtCreateTime'] = self.gmt_create_time - if self.gmt_modify_time is not None: - result['GmtModifyTime'] = self.gmt_modify_time - if self.mount_path is not None: - result['MountPath'] = self.mount_path if self.request_id is not None: result['RequestId'] = self.request_id - if self.user_id is not None: - result['UserId'] = self.user_id - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Accessibility') is not None: - self.accessibility = m.get('Accessibility') - if m.get('CodeBranch') is not None: - self.code_branch = m.get('CodeBranch') - if m.get('CodeCommit') is not None: - self.code_commit = m.get('CodeCommit') - if m.get('CodeRepo') is not None: - self.code_repo = m.get('CodeRepo') - if m.get('CodeRepoAccessToken') is not None: - self.code_repo_access_token = m.get('CodeRepoAccessToken') - if m.get('CodeRepoUserName') is not None: - self.code_repo_user_name = m.get('CodeRepoUserName') - if m.get('CodeSourceId') is not None: - self.code_source_id = m.get('CodeSourceId') - if m.get('Description') is not None: - self.description = m.get('Description') - if m.get('DisplayName') is not None: - self.display_name = m.get('DisplayName') - if m.get('GmtCreateTime') is not None: - self.gmt_create_time = m.get('GmtCreateTime') - if m.get('GmtModifyTime') is not None: - self.gmt_modify_time = m.get('GmtModifyTime') - if m.get('MountPath') is not None: - self.mount_path = m.get('MountPath') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('UserId') is not None: - self.user_id = m.get('UserId') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') return self -class GetCodeSourceResponse(TeaModel): +class DeleteModelResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetCodeSourceResponseBody = None, + body: DeleteModelResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -4844,17 +5475,17 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetCodeSourceResponseBody() + temp_model = DeleteModelResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetCodeSourcesStatisticsRequest(TeaModel): +class DeleteModelDomainRequest(TeaModel): def __init__( self, - workspace_id: str = None, + model_task_ids: str = None, ): - self.workspace_id = workspace_id + self.model_task_ids = model_task_ids def validate(self): pass @@ -4865,24 +5496,22 @@ def to_map(self): return _map result = dict() - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id + if self.model_task_ids is not None: + result['ModelTaskIds'] = self.model_task_ids return result def from_map(self, m: dict = None): m = m or dict() - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') + if m.get('ModelTaskIds') is not None: + self.model_task_ids = m.get('ModelTaskIds') return self -class GetCodeSourcesStatisticsResponseBody(TeaModel): +class DeleteModelDomainResponseBody(TeaModel): def __init__( self, - count: int = None, request_id: str = None, ): - self.count = count self.request_id = request_id def validate(self): @@ -4894,36 +5523,29 @@ def to_map(self): return _map result = dict() - if self.count is not None: - result['Count'] = self.count if self.request_id is not None: - result['requestId'] = self.request_id + result['RequestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Count') is not None: - self.count = m.get('Count') - if m.get('requestId') is not None: - self.request_id = m.get('requestId') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') return self -class GetCodeSourcesStatisticsResponse(TeaModel): +class DeleteModelDomainResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetCodeSourcesStatisticsResponseBody = None, + body: DeleteModelDomainResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -4948,59 +5570,53 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetCodeSourcesStatisticsResponseBody() + temp_model = DeleteModelDomainResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetDatasetResponseBody(TeaModel): +class DeleteModelLabelsRequest(TeaModel): + def __init__( + self, + keys: str = None, + label_keys: str = None, + ): + self.keys = keys + self.label_keys = label_keys + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.keys is not None: + result['Keys'] = self.keys + if self.label_keys is not None: + result['LabelKeys'] = self.label_keys + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Keys') is not None: + self.keys = m.get('Keys') + if m.get('LabelKeys') is not None: + self.label_keys = m.get('LabelKeys') + return self + + +class DeleteModelLabelsResponseBody(TeaModel): def __init__( self, - accessibility: str = None, - data_source_type: str = None, - data_type: str = None, - dataset_id: str = None, - description: str = None, - gmt_create_time: str = None, - gmt_modified_time: str = None, - labels: List[Label] = None, - name: str = None, - options: str = None, - owner_id: str = None, - property: str = None, - provider_type: str = None, request_id: str = None, - source_id: str = None, - source_type: str = None, - uri: str = None, - user_id: str = None, - workspace_id: str = None, ): - self.accessibility = accessibility - self.data_source_type = data_source_type - self.data_type = data_type - self.dataset_id = dataset_id - self.description = description - self.gmt_create_time = gmt_create_time - self.gmt_modified_time = gmt_modified_time - self.labels = labels - self.name = name - self.options = options - self.owner_id = owner_id - self.property = property - self.provider_type = provider_type self.request_id = request_id - self.source_id = source_id - self.source_type = source_type - self.uri = uri - self.user_id = user_id - self.workspace_id = workspace_id def validate(self): - if self.labels: - for k in self.labels: - if k: - k.validate() + pass def to_map(self): _map = super().to_map() @@ -5008,109 +5624,29 @@ def to_map(self): return _map result = dict() - if self.accessibility is not None: - result['Accessibility'] = self.accessibility - if self.data_source_type is not None: - result['DataSourceType'] = self.data_source_type - if self.data_type is not None: - result['DataType'] = self.data_type - if self.dataset_id is not None: - result['DatasetId'] = self.dataset_id - if self.description is not None: - result['Description'] = self.description - if self.gmt_create_time is not None: - result['GmtCreateTime'] = self.gmt_create_time - if self.gmt_modified_time is not None: - result['GmtModifiedTime'] = self.gmt_modified_time - result['Labels'] = [] - if self.labels is not None: - for k in self.labels: - result['Labels'].append(k.to_map() if k else None) - if self.name is not None: - result['Name'] = self.name - if self.options is not None: - result['Options'] = self.options - if self.owner_id is not None: - result['OwnerId'] = self.owner_id - if self.property is not None: - result['Property'] = self.property - if self.provider_type is not None: - result['ProviderType'] = self.provider_type if self.request_id is not None: result['RequestId'] = self.request_id - if self.source_id is not None: - result['SourceId'] = self.source_id - if self.source_type is not None: - result['SourceType'] = self.source_type - if self.uri is not None: - result['Uri'] = self.uri - if self.user_id is not None: - result['UserId'] = self.user_id - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Accessibility') is not None: - self.accessibility = m.get('Accessibility') - if m.get('DataSourceType') is not None: - self.data_source_type = m.get('DataSourceType') - if m.get('DataType') is not None: - self.data_type = m.get('DataType') - if m.get('DatasetId') is not None: - self.dataset_id = m.get('DatasetId') - if m.get('Description') is not None: - self.description = m.get('Description') - if m.get('GmtCreateTime') is not None: - self.gmt_create_time = m.get('GmtCreateTime') - if m.get('GmtModifiedTime') is not None: - self.gmt_modified_time = m.get('GmtModifiedTime') - self.labels = [] - if m.get('Labels') is not None: - for k in m.get('Labels'): - temp_model = Label() - self.labels.append(temp_model.from_map(k)) - if m.get('Name') is not None: - self.name = m.get('Name') - if m.get('Options') is not None: - self.options = m.get('Options') - if m.get('OwnerId') is not None: - self.owner_id = m.get('OwnerId') - if m.get('Property') is not None: - self.property = m.get('Property') - if m.get('ProviderType') is not None: - self.provider_type = m.get('ProviderType') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('SourceId') is not None: - self.source_id = m.get('SourceId') - if m.get('SourceType') is not None: - self.source_type = m.get('SourceType') - if m.get('Uri') is not None: - self.uri = m.get('Uri') - if m.get('UserId') is not None: - self.user_id = m.get('UserId') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') return self -class GetDatasetResponse(TeaModel): +class DeleteModelLabelsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetDatasetResponseBody = None, + body: DeleteModelLabelsResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -5135,45 +5671,16 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetDatasetResponseBody() + temp_model = DeleteModelLabelsResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetDatasetsStatisticsRequest(TeaModel): - def __init__( - self, - workspace_id: str = None, - ): - self.workspace_id = workspace_id - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') - return self - - -class GetDatasetsStatisticsResponseBody(TeaModel): +class DeleteModelVersionResponseBody(TeaModel): def __init__( self, - count: int = None, request_id: str = None, ): - self.count = count self.request_id = request_id def validate(self): @@ -5185,36 +5692,29 @@ def to_map(self): return _map result = dict() - if self.count is not None: - result['Count'] = self.count if self.request_id is not None: result['RequestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Count') is not None: - self.count = m.get('Count') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') return self -class GetDatasetsStatisticsResponse(TeaModel): +class DeleteModelVersionResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetDatasetsStatisticsResponseBody = None, + body: DeleteModelVersionResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -5239,17 +5739,19 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetDatasetsStatisticsResponseBody() + temp_model = DeleteModelVersionResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetDefaultWorkspaceRequest(TeaModel): +class DeleteModelVersionLabelsRequest(TeaModel): def __init__( self, - verbose: bool = None, + keys: str = None, + label_keys: str = None, ): - self.verbose = verbose + self.keys = keys + self.label_keys = label_keys def validate(self): pass @@ -5260,27 +5762,27 @@ def to_map(self): return _map result = dict() - if self.verbose is not None: - result['Verbose'] = self.verbose + if self.keys is not None: + result['Keys'] = self.keys + if self.label_keys is not None: + result['LabelKeys'] = self.label_keys return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Verbose') is not None: - self.verbose = m.get('Verbose') + if m.get('Keys') is not None: + self.keys = m.get('Keys') + if m.get('LabelKeys') is not None: + self.label_keys = m.get('LabelKeys') return self -class GetDefaultWorkspaceResponseBodyConditions(TeaModel): +class DeleteModelVersionLabelsResponseBody(TeaModel): def __init__( self, - code: int = None, - message: str = None, - type: str = None, + request_id: str = None, ): - self.code = code - self.message = message - self.type = type + self.request_id = request_id def validate(self): pass @@ -5291,38 +5793,31 @@ def to_map(self): return _map result = dict() - if self.code is not None: - result['Code'] = self.code - if self.message is not None: - result['Message'] = self.message - if self.type is not None: - result['Type'] = self.type + if self.request_id is not None: + result['RequestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Code') is not None: - self.code = m.get('Code') - if m.get('Message') is not None: - self.message = m.get('Message') - if m.get('Type') is not None: - self.type = m.get('Type') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') return self -class GetDefaultWorkspaceResponseBodyOwner(TeaModel): +class DeleteModelVersionLabelsResponse(TeaModel): def __init__( self, - user_id: str = None, - user_kp: str = None, - user_name: str = None, + headers: Dict[str, str] = None, + status_code: int = None, + body: DeleteModelVersionLabelsResponseBody = None, ): - self.user_id = user_id - self.user_kp = user_kp - self.user_name = user_name + self.headers = headers + self.status_code = status_code + self.body = body def validate(self): - pass + if self.body: + self.body.validate() def to_map(self): _map = super().to_map() @@ -5330,61 +5825,35 @@ def to_map(self): return _map result = dict() - if self.user_id is not None: - result['UserId'] = self.user_id - if self.user_kp is not None: - result['UserKp'] = self.user_kp - if self.user_name is not None: - result['UserName'] = self.user_name + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() return result def from_map(self, m: dict = None): m = m or dict() - if m.get('UserId') is not None: - self.user_id = m.get('UserId') - if m.get('UserKp') is not None: - self.user_kp = m.get('UserKp') - if m.get('UserName') is not None: - self.user_name = m.get('UserName') + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = DeleteModelVersionLabelsResponseBody() + self.body = temp_model.from_map(m['body']) return self -class GetDefaultWorkspaceResponseBody(TeaModel): +class DeleteServiceTemplateResponseBody(TeaModel): def __init__( self, - conditions: List[GetDefaultWorkspaceResponseBodyConditions] = None, - creator: str = None, - description: str = None, - display_name: str = None, - env_types: List[str] = None, - gmt_create_time: str = None, - gmt_modified_time: str = None, - owner: GetDefaultWorkspaceResponseBodyOwner = None, request_id: str = None, - status: str = None, - workspace_id: str = None, - workspace_name: str = None, ): - self.conditions = conditions - self.creator = creator - self.description = description - self.display_name = display_name - self.env_types = env_types - self.gmt_create_time = gmt_create_time - self.gmt_modified_time = gmt_modified_time - self.owner = owner self.request_id = request_id - self.status = status - self.workspace_id = workspace_id - self.workspace_name = workspace_name def validate(self): - if self.conditions: - for k in self.conditions: - if k: - k.validate() - if self.owner: - self.owner.validate() + pass def to_map(self): _map = super().to_map() @@ -5392,82 +5861,29 @@ def to_map(self): return _map result = dict() - result['Conditions'] = [] - if self.conditions is not None: - for k in self.conditions: - result['Conditions'].append(k.to_map() if k else None) - if self.creator is not None: - result['Creator'] = self.creator - if self.description is not None: - result['Description'] = self.description - if self.display_name is not None: - result['DisplayName'] = self.display_name - if self.env_types is not None: - result['EnvTypes'] = self.env_types - if self.gmt_create_time is not None: - result['GmtCreateTime'] = self.gmt_create_time - if self.gmt_modified_time is not None: - result['GmtModifiedTime'] = self.gmt_modified_time - if self.owner is not None: - result['Owner'] = self.owner.to_map() if self.request_id is not None: result['RequestId'] = self.request_id - if self.status is not None: - result['Status'] = self.status - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id - if self.workspace_name is not None: - result['WorkspaceName'] = self.workspace_name return result def from_map(self, m: dict = None): m = m or dict() - self.conditions = [] - if m.get('Conditions') is not None: - for k in m.get('Conditions'): - temp_model = GetDefaultWorkspaceResponseBodyConditions() - self.conditions.append(temp_model.from_map(k)) - if m.get('Creator') is not None: - self.creator = m.get('Creator') - if m.get('Description') is not None: - self.description = m.get('Description') - if m.get('DisplayName') is not None: - self.display_name = m.get('DisplayName') - if m.get('EnvTypes') is not None: - self.env_types = m.get('EnvTypes') - if m.get('GmtCreateTime') is not None: - self.gmt_create_time = m.get('GmtCreateTime') - if m.get('GmtModifiedTime') is not None: - self.gmt_modified_time = m.get('GmtModifiedTime') - if m.get('Owner') is not None: - temp_model = GetDefaultWorkspaceResponseBodyOwner() - self.owner = temp_model.from_map(m['Owner']) if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('Status') is not None: - self.status = m.get('Status') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') - if m.get('WorkspaceName') is not None: - self.workspace_name = m.get('WorkspaceName') return self -class GetDefaultWorkspaceResponse(TeaModel): +class DeleteServiceTemplateResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetDefaultWorkspaceResponseBody = None, + body: DeleteServiceTemplateResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -5492,17 +5908,17 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetDefaultWorkspaceResponseBody() + temp_model = DeleteServiceTemplateResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetImageRequest(TeaModel): +class DeleteServiceTemplateLabelsRequest(TeaModel): def __init__( self, - verbose: bool = None, + label_keys: str = None, ): - self.verbose = verbose + self.label_keys = label_keys def validate(self): pass @@ -5513,25 +5929,23 @@ def to_map(self): return _map result = dict() - if self.verbose is not None: - result['Verbose'] = self.verbose + if self.label_keys is not None: + result['LabelKeys'] = self.label_keys return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Verbose') is not None: - self.verbose = m.get('Verbose') + if m.get('LabelKeys') is not None: + self.label_keys = m.get('LabelKeys') return self -class GetImageResponseBodyLabels(TeaModel): +class DeleteServiceTemplateLabelsResponseBody(TeaModel): def __init__( self, - key: str = None, - value: str = None, + request_id: str = None, ): - self.key = key - self.value = value + self.request_id = request_id def validate(self): pass @@ -5542,53 +5956,94 @@ def to_map(self): return _map result = dict() - if self.key is not None: - result['Key'] = self.key - if self.value is not None: - result['Value'] = self.value + if self.request_id is not None: + result['RequestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Key') is not None: - self.key = m.get('Key') - if m.get('Value') is not None: - self.value = m.get('Value') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') return self -class GetImageResponseBody(TeaModel): +class DeleteServiceTemplateLabelsResponse(TeaModel): def __init__( self, - accessibility: str = None, - description: str = None, - gmt_create_time: str = None, - gmt_modified_time: str = None, - image_uri: str = None, - labels: List[GetImageResponseBodyLabels] = None, - name: str = None, - parent_user_id: str = None, - request_id: str = None, - user_id: str = None, - workspace_id: str = None, + headers: Dict[str, str] = None, + status_code: int = None, + body: DeleteServiceTemplateLabelsResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = DeleteServiceTemplateLabelsResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class DeleteUserConfigRequest(TeaModel): + def __init__( + self, + config_key: str = None, + ): + self.config_key = config_key + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.config_key is not None: + result['ConfigKey'] = self.config_key + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('ConfigKey') is not None: + self.config_key = m.get('ConfigKey') + return self + + +class DeleteUserConfigResponseBody(TeaModel): + def __init__( + self, + request_id: str = None, ): - self.accessibility = accessibility - self.description = description - self.gmt_create_time = gmt_create_time - self.gmt_modified_time = gmt_modified_time - self.image_uri = image_uri - self.labels = labels - self.name = name - self.parent_user_id = parent_user_id self.request_id = request_id - self.user_id = user_id - self.workspace_id = workspace_id def validate(self): - if self.labels: - for k in self.labels: - if k: - k.validate() + pass def to_map(self): _map = super().to_map() @@ -5596,77 +6051,29 @@ def to_map(self): return _map result = dict() - if self.accessibility is not None: - result['Accessibility'] = self.accessibility - if self.description is not None: - result['Description'] = self.description - if self.gmt_create_time is not None: - result['GmtCreateTime'] = self.gmt_create_time - if self.gmt_modified_time is not None: - result['GmtModifiedTime'] = self.gmt_modified_time - if self.image_uri is not None: - result['ImageUri'] = self.image_uri - result['Labels'] = [] - if self.labels is not None: - for k in self.labels: - result['Labels'].append(k.to_map() if k else None) - if self.name is not None: - result['Name'] = self.name - if self.parent_user_id is not None: - result['ParentUserId'] = self.parent_user_id if self.request_id is not None: result['RequestId'] = self.request_id - if self.user_id is not None: - result['UserId'] = self.user_id - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Accessibility') is not None: - self.accessibility = m.get('Accessibility') - if m.get('Description') is not None: - self.description = m.get('Description') - if m.get('GmtCreateTime') is not None: - self.gmt_create_time = m.get('GmtCreateTime') - if m.get('GmtModifiedTime') is not None: - self.gmt_modified_time = m.get('GmtModifiedTime') - if m.get('ImageUri') is not None: - self.image_uri = m.get('ImageUri') - self.labels = [] - if m.get('Labels') is not None: - for k in m.get('Labels'): - temp_model = GetImageResponseBodyLabels() - self.labels.append(temp_model.from_map(k)) - if m.get('Name') is not None: - self.name = m.get('Name') - if m.get('ParentUserId') is not None: - self.parent_user_id = m.get('ParentUserId') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('UserId') is not None: - self.user_id = m.get('UserId') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') return self -class GetImageResponse(TeaModel): +class DeleteUserConfigResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetImageResponseBody = None, + body: DeleteUserConfigResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -5691,45 +6098,16 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetImageResponseBody() + temp_model = DeleteUserConfigResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetImagesStatisticsRequest(TeaModel): - def __init__( - self, - workspace_id: str = None, - ): - self.workspace_id = workspace_id - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') - return self - - -class GetImagesStatisticsResponseBody(TeaModel): +class DeleteWorkspaceResponseBody(TeaModel): def __init__( self, - count: int = None, request_id: str = None, ): - self.count = count self.request_id = request_id def validate(self): @@ -5741,36 +6119,29 @@ def to_map(self): return _map result = dict() - if self.count is not None: - result['Count'] = self.count if self.request_id is not None: result['RequestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Count') is not None: - self.count = m.get('Count') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') return self -class GetImagesStatisticsResponse(TeaModel): +class DeleteWorkspaceResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetImagesStatisticsResponseBody = None, + body: DeleteWorkspaceResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -5795,17 +6166,27 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetImagesStatisticsResponseBody() + temp_model = DeleteWorkspaceResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetMemberRequest(TeaModel): +class DeleteWorkspaceResourceRequest(TeaModel): def __init__( self, - user_id: str = None, + group_name: str = None, + labels: str = None, + option: str = None, + product_type: str = None, + resource_ids: str = None, + resource_type: str = None, ): - self.user_id = user_id + self.group_name = group_name + self.labels = labels + self.option = option + self.product_type = product_type + self.resource_ids = resource_ids + self.resource_type = resource_type def validate(self): pass @@ -5816,35 +6197,45 @@ def to_map(self): return _map result = dict() - if self.user_id is not None: - result['UserId'] = self.user_id + if self.group_name is not None: + result['GroupName'] = self.group_name + if self.labels is not None: + result['Labels'] = self.labels + if self.option is not None: + result['Option'] = self.option + if self.product_type is not None: + result['ProductType'] = self.product_type + if self.resource_ids is not None: + result['ResourceIds'] = self.resource_ids + if self.resource_type is not None: + result['ResourceType'] = self.resource_type return result def from_map(self, m: dict = None): m = m or dict() - if m.get('UserId') is not None: - self.user_id = m.get('UserId') + if m.get('GroupName') is not None: + self.group_name = m.get('GroupName') + if m.get('Labels') is not None: + self.labels = m.get('Labels') + if m.get('Option') is not None: + self.option = m.get('Option') + if m.get('ProductType') is not None: + self.product_type = m.get('ProductType') + if m.get('ResourceIds') is not None: + self.resource_ids = m.get('ResourceIds') + if m.get('ResourceType') is not None: + self.resource_type = m.get('ResourceType') return self -class GetMemberResponseBody(TeaModel): +class DeleteWorkspaceResourceResponseBody(TeaModel): def __init__( self, - display_name: str = None, - gmt_create_time: str = None, - member_id: str = None, - member_name: str = None, request_id: str = None, - roles: List[str] = None, - user_id: str = None, + resource_ids: List[str] = None, ): - self.display_name = display_name - self.gmt_create_time = gmt_create_time - self.member_id = member_id - self.member_name = member_name self.request_id = request_id - self.roles = roles - self.user_id = user_id + self.resource_ids = resource_ids def validate(self): pass @@ -5855,56 +6246,33 @@ def to_map(self): return _map result = dict() - if self.display_name is not None: - result['DisplayName'] = self.display_name - if self.gmt_create_time is not None: - result['GmtCreateTime'] = self.gmt_create_time - if self.member_id is not None: - result['MemberId'] = self.member_id - if self.member_name is not None: - result['MemberName'] = self.member_name if self.request_id is not None: result['RequestId'] = self.request_id - if self.roles is not None: - result['Roles'] = self.roles - if self.user_id is not None: - result['UserId'] = self.user_id + if self.resource_ids is not None: + result['ResourceIds'] = self.resource_ids return result def from_map(self, m: dict = None): m = m or dict() - if m.get('DisplayName') is not None: - self.display_name = m.get('DisplayName') - if m.get('GmtCreateTime') is not None: - self.gmt_create_time = m.get('GmtCreateTime') - if m.get('MemberId') is not None: - self.member_id = m.get('MemberId') - if m.get('MemberName') is not None: - self.member_name = m.get('MemberName') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('Roles') is not None: - self.roles = m.get('Roles') - if m.get('UserId') is not None: - self.user_id = m.get('UserId') + if m.get('ResourceIds') is not None: + self.resource_ids = m.get('ResourceIds') return self -class GetMemberResponse(TeaModel): +class DeleteWorkspaceResourceResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetMemberResponseBody = None, + body: DeleteWorkspaceResourceResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -5929,57 +6297,3789 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetMemberResponseBody() + temp_model = DeleteWorkspaceResourceResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetModelResponseBody(TeaModel): +class DescribePricingModuleRequest(TeaModel): def __init__( self, - accessibility: str = None, - domain: str = None, - gmt_create_time: str = None, - gmt_modified_time: str = None, - labels: List[Label] = None, - latest_version: ModelVersion = None, - model_description: str = None, - model_doc: str = None, - model_id: str = None, - model_name: str = None, - origin: str = None, - owner_id: str = None, - provider: str = None, + product_code: str = None, + product_type: str = None, + subscription_type: str = None, + ): + self.product_code = product_code + self.product_type = product_type + self.subscription_type = subscription_type + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.product_code is not None: + result['ProductCode'] = self.product_code + if self.product_type is not None: + result['ProductType'] = self.product_type + if self.subscription_type is not None: + result['SubscriptionType'] = self.subscription_type + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('ProductCode') is not None: + self.product_code = m.get('ProductCode') + if m.get('ProductType') is not None: + self.product_type = m.get('ProductType') + if m.get('SubscriptionType') is not None: + self.subscription_type = m.get('SubscriptionType') + return self + + +class DescribePricingModuleResponseBodyAttributeListValues(TeaModel): + def __init__( + self, + name: str = None, + remark: str = None, + type: str = None, + value: str = None, + ): + self.name = name + self.remark = remark + self.type = type + self.value = value + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.name is not None: + result['Name'] = self.name + if self.remark is not None: + result['Remark'] = self.remark + if self.type is not None: + result['Type'] = self.type + if self.value is not None: + result['Value'] = self.value + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('Remark') is not None: + self.remark = m.get('Remark') + if m.get('Type') is not None: + self.type = m.get('Type') + if m.get('Value') is not None: + self.value = m.get('Value') + return self + + +class DescribePricingModuleResponseBodyAttributeList(TeaModel): + def __init__( + self, + code: str = None, + name: str = None, + unit: str = None, + values: List[DescribePricingModuleResponseBodyAttributeListValues] = None, + ): + self.code = code + self.name = name + self.unit = unit + self.values = values + + def validate(self): + if self.values: + for k in self.values: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.code is not None: + result['Code'] = self.code + if self.name is not None: + result['Name'] = self.name + if self.unit is not None: + result['Unit'] = self.unit + result['Values'] = [] + if self.values is not None: + for k in self.values: + result['Values'].append(k.to_map() if k else None) + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Code') is not None: + self.code = m.get('Code') + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('Unit') is not None: + self.unit = m.get('Unit') + self.values = [] + if m.get('Values') is not None: + for k in m.get('Values'): + temp_model = DescribePricingModuleResponseBodyAttributeListValues() + self.values.append(temp_model.from_map(k)) + return self + + +class DescribePricingModuleResponseBodyModuleList(TeaModel): + def __init__( + self, + config_list: List[str] = None, + currency: str = None, + module_code: str = None, + module_name: str = None, + price_type: str = None, + ): + self.config_list = config_list + self.currency = currency + self.module_code = module_code + self.module_name = module_name + self.price_type = price_type + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.config_list is not None: + result['ConfigList'] = self.config_list + if self.currency is not None: + result['Currency'] = self.currency + if self.module_code is not None: + result['ModuleCode'] = self.module_code + if self.module_name is not None: + result['ModuleName'] = self.module_name + if self.price_type is not None: + result['PriceType'] = self.price_type + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('ConfigList') is not None: + self.config_list = m.get('ConfigList') + if m.get('Currency') is not None: + self.currency = m.get('Currency') + if m.get('ModuleCode') is not None: + self.module_code = m.get('ModuleCode') + if m.get('ModuleName') is not None: + self.module_name = m.get('ModuleName') + if m.get('PriceType') is not None: + self.price_type = m.get('PriceType') + return self + + +class DescribePricingModuleResponseBody(TeaModel): + def __init__( + self, + attribute_list: List[DescribePricingModuleResponseBodyAttributeList] = None, + module_list: List[DescribePricingModuleResponseBodyModuleList] = None, + request_id: str = None, + ): + self.attribute_list = attribute_list + self.module_list = module_list + self.request_id = request_id + + def validate(self): + if self.attribute_list: + for k in self.attribute_list: + if k: + k.validate() + if self.module_list: + for k in self.module_list: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + result['AttributeList'] = [] + if self.attribute_list is not None: + for k in self.attribute_list: + result['AttributeList'].append(k.to_map() if k else None) + result['ModuleList'] = [] + if self.module_list is not None: + for k in self.module_list: + result['ModuleList'].append(k.to_map() if k else None) + if self.request_id is not None: + result['RequestId'] = self.request_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + self.attribute_list = [] + if m.get('AttributeList') is not None: + for k in m.get('AttributeList'): + temp_model = DescribePricingModuleResponseBodyAttributeList() + self.attribute_list.append(temp_model.from_map(k)) + self.module_list = [] + if m.get('ModuleList') is not None: + for k in m.get('ModuleList'): + temp_model = DescribePricingModuleResponseBodyModuleList() + self.module_list.append(temp_model.from_map(k)) + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + return self + + +class DescribePricingModuleResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: DescribePricingModuleResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = DescribePricingModuleResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetCodeSourceResponseBody(TeaModel): + def __init__( + self, + accessibility: str = None, + code_branch: str = None, + code_commit: str = None, + code_repo: str = None, + code_repo_access_token: str = None, + code_repo_user_name: str = None, + code_source_id: str = None, + description: str = None, + display_name: str = None, + gmt_create_time: str = None, + gmt_modify_time: str = None, + mount_path: str = None, request_id: str = None, - task: str = None, user_id: str = None, workspace_id: str = None, ): - self.accessibility = accessibility - self.domain = domain - self.gmt_create_time = gmt_create_time - self.gmt_modified_time = gmt_modified_time + self.accessibility = accessibility + self.code_branch = code_branch + self.code_commit = code_commit + self.code_repo = code_repo + self.code_repo_access_token = code_repo_access_token + self.code_repo_user_name = code_repo_user_name + self.code_source_id = code_source_id + self.description = description + self.display_name = display_name + self.gmt_create_time = gmt_create_time + self.gmt_modify_time = gmt_modify_time + self.mount_path = mount_path + self.request_id = request_id + self.user_id = user_id + self.workspace_id = workspace_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.accessibility is not None: + result['Accessibility'] = self.accessibility + if self.code_branch is not None: + result['CodeBranch'] = self.code_branch + if self.code_commit is not None: + result['CodeCommit'] = self.code_commit + if self.code_repo is not None: + result['CodeRepo'] = self.code_repo + if self.code_repo_access_token is not None: + result['CodeRepoAccessToken'] = self.code_repo_access_token + if self.code_repo_user_name is not None: + result['CodeRepoUserName'] = self.code_repo_user_name + if self.code_source_id is not None: + result['CodeSourceId'] = self.code_source_id + if self.description is not None: + result['Description'] = self.description + if self.display_name is not None: + result['DisplayName'] = self.display_name + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modify_time is not None: + result['GmtModifyTime'] = self.gmt_modify_time + if self.mount_path is not None: + result['MountPath'] = self.mount_path + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.user_id is not None: + result['UserId'] = self.user_id + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Accessibility') is not None: + self.accessibility = m.get('Accessibility') + if m.get('CodeBranch') is not None: + self.code_branch = m.get('CodeBranch') + if m.get('CodeCommit') is not None: + self.code_commit = m.get('CodeCommit') + if m.get('CodeRepo') is not None: + self.code_repo = m.get('CodeRepo') + if m.get('CodeRepoAccessToken') is not None: + self.code_repo_access_token = m.get('CodeRepoAccessToken') + if m.get('CodeRepoUserName') is not None: + self.code_repo_user_name = m.get('CodeRepoUserName') + if m.get('CodeSourceId') is not None: + self.code_source_id = m.get('CodeSourceId') + if m.get('Description') is not None: + self.description = m.get('Description') + if m.get('DisplayName') is not None: + self.display_name = m.get('DisplayName') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifyTime') is not None: + self.gmt_modify_time = m.get('GmtModifyTime') + if m.get('MountPath') is not None: + self.mount_path = m.get('MountPath') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') + return self + + +class GetCodeSourceResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetCodeSourceResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetCodeSourceResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetCodeSourcesStatisticsRequest(TeaModel): + def __init__( + self, + workspace_id: str = None, + ): + self.workspace_id = workspace_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') + return self + + +class GetCodeSourcesStatisticsResponseBody(TeaModel): + def __init__( + self, + count: int = None, + request_id: str = None, + ): + self.count = count + self.request_id = request_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.count is not None: + result['Count'] = self.count + if self.request_id is not None: + result['requestId'] = self.request_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Count') is not None: + self.count = m.get('Count') + if m.get('requestId') is not None: + self.request_id = m.get('requestId') + return self + + +class GetCodeSourcesStatisticsResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetCodeSourcesStatisticsResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetCodeSourcesStatisticsResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetCollectionResponseBody(TeaModel): + def __init__( + self, + collection_name: str = None, + gmt_create_time: str = None, + gmt_modified_time: str = None, + owner_id: str = None, + request_id: str = None, + user_id: str = None, + ): + self.collection_name = collection_name + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time + self.owner_id = owner_id + self.request_id = request_id + self.user_id = user_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.collection_name is not None: + result['CollectionName'] = self.collection_name + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + if self.owner_id is not None: + result['OwnerId'] = self.owner_id + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.user_id is not None: + result['UserId'] = self.user_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('CollectionName') is not None: + self.collection_name = m.get('CollectionName') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + if m.get('OwnerId') is not None: + self.owner_id = m.get('OwnerId') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + return self + + +class GetCollectionResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetCollectionResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetCollectionResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetDatasetResponseBody(TeaModel): + def __init__( + self, + accessibility: str = None, + data_source_type: str = None, + data_type: str = None, + dataset_id: str = None, + description: str = None, + gmt_create_time: str = None, + gmt_modified_time: str = None, + labels: List[Label] = None, + name: str = None, + options: str = None, + owner_id: str = None, + property: str = None, + provider_type: str = None, + request_id: str = None, + source_id: str = None, + source_type: str = None, + uri: str = None, + user_id: str = None, + workspace_id: str = None, + ): + self.accessibility = accessibility + self.data_source_type = data_source_type + self.data_type = data_type + self.dataset_id = dataset_id + self.description = description + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time + self.labels = labels + self.name = name + self.options = options + self.owner_id = owner_id + self.property = property + self.provider_type = provider_type + self.request_id = request_id + self.source_id = source_id + self.source_type = source_type + self.uri = uri + self.user_id = user_id + self.workspace_id = workspace_id + + def validate(self): + if self.labels: + for k in self.labels: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.accessibility is not None: + result['Accessibility'] = self.accessibility + if self.data_source_type is not None: + result['DataSourceType'] = self.data_source_type + if self.data_type is not None: + result['DataType'] = self.data_type + if self.dataset_id is not None: + result['DatasetId'] = self.dataset_id + if self.description is not None: + result['Description'] = self.description + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + if self.name is not None: + result['Name'] = self.name + if self.options is not None: + result['Options'] = self.options + if self.owner_id is not None: + result['OwnerId'] = self.owner_id + if self.property is not None: + result['Property'] = self.property + if self.provider_type is not None: + result['ProviderType'] = self.provider_type + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.source_id is not None: + result['SourceId'] = self.source_id + if self.source_type is not None: + result['SourceType'] = self.source_type + if self.uri is not None: + result['Uri'] = self.uri + if self.user_id is not None: + result['UserId'] = self.user_id + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Accessibility') is not None: + self.accessibility = m.get('Accessibility') + if m.get('DataSourceType') is not None: + self.data_source_type = m.get('DataSourceType') + if m.get('DataType') is not None: + self.data_type = m.get('DataType') + if m.get('DatasetId') is not None: + self.dataset_id = m.get('DatasetId') + if m.get('Description') is not None: + self.description = m.get('Description') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = Label() + self.labels.append(temp_model.from_map(k)) + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('Options') is not None: + self.options = m.get('Options') + if m.get('OwnerId') is not None: + self.owner_id = m.get('OwnerId') + if m.get('Property') is not None: + self.property = m.get('Property') + if m.get('ProviderType') is not None: + self.provider_type = m.get('ProviderType') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('SourceId') is not None: + self.source_id = m.get('SourceId') + if m.get('SourceType') is not None: + self.source_type = m.get('SourceType') + if m.get('Uri') is not None: + self.uri = m.get('Uri') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') + return self + + +class GetDatasetResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetDatasetResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetDatasetResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetDatasetsStatisticsRequest(TeaModel): + def __init__( + self, + workspace_id: str = None, + ): + self.workspace_id = workspace_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') + return self + + +class GetDatasetsStatisticsResponseBody(TeaModel): + def __init__( + self, + count: int = None, + request_id: str = None, + ): + self.count = count + self.request_id = request_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.count is not None: + result['Count'] = self.count + if self.request_id is not None: + result['RequestId'] = self.request_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Count') is not None: + self.count = m.get('Count') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + return self + + +class GetDatasetsStatisticsResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetDatasetsStatisticsResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetDatasetsStatisticsResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetDefaultWorkspaceRequest(TeaModel): + def __init__( + self, + verbose: bool = None, + ): + self.verbose = verbose + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.verbose is not None: + result['Verbose'] = self.verbose + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Verbose') is not None: + self.verbose = m.get('Verbose') + return self + + +class GetDefaultWorkspaceResponseBodyConditions(TeaModel): + def __init__( + self, + code: int = None, + message: str = None, + type: str = None, + ): + self.code = code + self.message = message + self.type = type + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.code is not None: + result['Code'] = self.code + if self.message is not None: + result['Message'] = self.message + if self.type is not None: + result['Type'] = self.type + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Code') is not None: + self.code = m.get('Code') + if m.get('Message') is not None: + self.message = m.get('Message') + if m.get('Type') is not None: + self.type = m.get('Type') + return self + + +class GetDefaultWorkspaceResponseBodyOwner(TeaModel): + def __init__( + self, + user_id: str = None, + user_kp: str = None, + user_name: str = None, + ): + self.user_id = user_id + self.user_kp = user_kp + self.user_name = user_name + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.user_id is not None: + result['UserId'] = self.user_id + if self.user_kp is not None: + result['UserKp'] = self.user_kp + if self.user_name is not None: + result['UserName'] = self.user_name + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + if m.get('UserKp') is not None: + self.user_kp = m.get('UserKp') + if m.get('UserName') is not None: + self.user_name = m.get('UserName') + return self + + +class GetDefaultWorkspaceResponseBody(TeaModel): + def __init__( + self, + conditions: List[GetDefaultWorkspaceResponseBodyConditions] = None, + creator: str = None, + description: str = None, + display_name: str = None, + env_types: List[str] = None, + gmt_create_time: str = None, + gmt_modified_time: str = None, + owner: GetDefaultWorkspaceResponseBodyOwner = None, + request_id: str = None, + status: str = None, + workspace_id: str = None, + workspace_name: str = None, + ): + self.conditions = conditions + self.creator = creator + self.description = description + self.display_name = display_name + self.env_types = env_types + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time + self.owner = owner + self.request_id = request_id + self.status = status + self.workspace_id = workspace_id + self.workspace_name = workspace_name + + def validate(self): + if self.conditions: + for k in self.conditions: + if k: + k.validate() + if self.owner: + self.owner.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + result['Conditions'] = [] + if self.conditions is not None: + for k in self.conditions: + result['Conditions'].append(k.to_map() if k else None) + if self.creator is not None: + result['Creator'] = self.creator + if self.description is not None: + result['Description'] = self.description + if self.display_name is not None: + result['DisplayName'] = self.display_name + if self.env_types is not None: + result['EnvTypes'] = self.env_types + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + if self.owner is not None: + result['Owner'] = self.owner.to_map() + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.status is not None: + result['Status'] = self.status + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id + if self.workspace_name is not None: + result['WorkspaceName'] = self.workspace_name + return result + + def from_map(self, m: dict = None): + m = m or dict() + self.conditions = [] + if m.get('Conditions') is not None: + for k in m.get('Conditions'): + temp_model = GetDefaultWorkspaceResponseBodyConditions() + self.conditions.append(temp_model.from_map(k)) + if m.get('Creator') is not None: + self.creator = m.get('Creator') + if m.get('Description') is not None: + self.description = m.get('Description') + if m.get('DisplayName') is not None: + self.display_name = m.get('DisplayName') + if m.get('EnvTypes') is not None: + self.env_types = m.get('EnvTypes') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + if m.get('Owner') is not None: + temp_model = GetDefaultWorkspaceResponseBodyOwner() + self.owner = temp_model.from_map(m['Owner']) + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('Status') is not None: + self.status = m.get('Status') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') + if m.get('WorkspaceName') is not None: + self.workspace_name = m.get('WorkspaceName') + return self + + +class GetDefaultWorkspaceResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetDefaultWorkspaceResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetDefaultWorkspaceResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetExperimentResponseBody(TeaModel): + def __init__( + self, + accessibility: str = None, + artifact_uri: str = None, + experiment_id: str = None, + gmt_create_time: str = None, + gmt_modified_time: str = None, + labels: List[ExperimentLabel] = None, + name: str = None, + owner_id: str = None, + request_id: str = None, + tensorboard_log_uri: str = None, + user_id: str = None, + workspace_id: str = None, + ): + self.accessibility = accessibility + # Artifact的OSS存储路径 + self.artifact_uri = artifact_uri + # 实验UUID + self.experiment_id = experiment_id + # 创建时间 + self.gmt_create_time = gmt_create_time + # 更新时间 + self.gmt_modified_time = gmt_modified_time + # 标签 + self.labels = labels + # 名称 + self.name = name + # 拥有者ID + self.owner_id = owner_id + self.request_id = request_id + # tensorboard日志OSS存储路径 + self.tensorboard_log_uri = tensorboard_log_uri + # 创建者ID + self.user_id = user_id + # 工作空间ID + self.workspace_id = workspace_id + + def validate(self): + if self.labels: + for k in self.labels: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.accessibility is not None: + result['Accessibility'] = self.accessibility + if self.artifact_uri is not None: + result['ArtifactUri'] = self.artifact_uri + if self.experiment_id is not None: + result['ExperimentId'] = self.experiment_id + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + if self.name is not None: + result['Name'] = self.name + if self.owner_id is not None: + result['OwnerId'] = self.owner_id + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.tensorboard_log_uri is not None: + result['TensorboardLogUri'] = self.tensorboard_log_uri + if self.user_id is not None: + result['UserId'] = self.user_id + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Accessibility') is not None: + self.accessibility = m.get('Accessibility') + if m.get('ArtifactUri') is not None: + self.artifact_uri = m.get('ArtifactUri') + if m.get('ExperimentId') is not None: + self.experiment_id = m.get('ExperimentId') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = ExperimentLabel() + self.labels.append(temp_model.from_map(k)) + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('OwnerId') is not None: + self.owner_id = m.get('OwnerId') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('TensorboardLogUri') is not None: + self.tensorboard_log_uri = m.get('TensorboardLogUri') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') + return self + + +class GetExperimentResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetExperimentResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetExperimentResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetImageRequest(TeaModel): + def __init__( + self, + verbose: bool = None, + ): + self.verbose = verbose + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.verbose is not None: + result['Verbose'] = self.verbose + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Verbose') is not None: + self.verbose = m.get('Verbose') + return self + + +class GetImageResponseBodyLabels(TeaModel): + def __init__( + self, + key: str = None, + value: str = None, + ): + self.key = key + self.value = value + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.key is not None: + result['Key'] = self.key + if self.value is not None: + result['Value'] = self.value + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Key') is not None: + self.key = m.get('Key') + if m.get('Value') is not None: + self.value = m.get('Value') + return self + + +class GetImageResponseBody(TeaModel): + def __init__( + self, + accessibility: str = None, + description: str = None, + gmt_create_time: str = None, + gmt_modified_time: str = None, + image_uri: str = None, + labels: List[GetImageResponseBodyLabels] = None, + name: str = None, + parent_user_id: str = None, + request_id: str = None, + size: int = None, + user_id: str = None, + workspace_id: str = None, + ): + self.accessibility = accessibility + self.description = description + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time + self.image_uri = image_uri + self.labels = labels + self.name = name + self.parent_user_id = parent_user_id + self.request_id = request_id + self.size = size + self.user_id = user_id + self.workspace_id = workspace_id + + def validate(self): + if self.labels: + for k in self.labels: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.accessibility is not None: + result['Accessibility'] = self.accessibility + if self.description is not None: + result['Description'] = self.description + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + if self.image_uri is not None: + result['ImageUri'] = self.image_uri + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + if self.name is not None: + result['Name'] = self.name + if self.parent_user_id is not None: + result['ParentUserId'] = self.parent_user_id + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.size is not None: + result['Size'] = self.size + if self.user_id is not None: + result['UserId'] = self.user_id + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Accessibility') is not None: + self.accessibility = m.get('Accessibility') + if m.get('Description') is not None: + self.description = m.get('Description') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + if m.get('ImageUri') is not None: + self.image_uri = m.get('ImageUri') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = GetImageResponseBodyLabels() + self.labels.append(temp_model.from_map(k)) + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('ParentUserId') is not None: + self.parent_user_id = m.get('ParentUserId') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('Size') is not None: + self.size = m.get('Size') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') + return self + + +class GetImageResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetImageResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetImageResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetImagesStatisticsRequest(TeaModel): + def __init__( + self, + workspace_id: str = None, + ): + self.workspace_id = workspace_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') + return self + + +class GetImagesStatisticsResponseBody(TeaModel): + def __init__( + self, + count: int = None, + request_id: str = None, + ): + self.count = count + self.request_id = request_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.count is not None: + result['Count'] = self.count + if self.request_id is not None: + result['RequestId'] = self.request_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Count') is not None: + self.count = m.get('Count') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + return self + + +class GetImagesStatisticsResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetImagesStatisticsResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetImagesStatisticsResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetMemberRequest(TeaModel): + def __init__( + self, + member_id: str = None, + user_id: str = None, + ): + self.member_id = member_id + self.user_id = user_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.member_id is not None: + result['MemberId'] = self.member_id + if self.user_id is not None: + result['UserId'] = self.user_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('MemberId') is not None: + self.member_id = m.get('MemberId') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + return self + + +class GetMemberResponseBody(TeaModel): + def __init__( + self, + display_name: str = None, + gmt_create_time: str = None, + member_id: str = None, + member_name: str = None, + request_id: str = None, + roles: List[str] = None, + user_id: str = None, + ): + self.display_name = display_name + self.gmt_create_time = gmt_create_time + self.member_id = member_id + self.member_name = member_name + self.request_id = request_id + self.roles = roles + self.user_id = user_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.display_name is not None: + result['DisplayName'] = self.display_name + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.member_id is not None: + result['MemberId'] = self.member_id + if self.member_name is not None: + result['MemberName'] = self.member_name + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.roles is not None: + result['Roles'] = self.roles + if self.user_id is not None: + result['UserId'] = self.user_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('DisplayName') is not None: + self.display_name = m.get('DisplayName') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('MemberId') is not None: + self.member_id = m.get('MemberId') + if m.get('MemberName') is not None: + self.member_name = m.get('MemberName') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('Roles') is not None: + self.roles = m.get('Roles') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + return self + + +class GetMemberResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetMemberResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetMemberResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetModelResponseBody(TeaModel): + def __init__( + self, + accessibility: str = None, + domain: str = None, + extra_info: Dict[str, Any] = None, + gmt_create_time: str = None, + gmt_modified_time: str = None, + labels: List[Label] = None, + latest_version: ModelVersion = None, + model_description: str = None, + model_doc: str = None, + model_id: str = None, + model_name: str = None, + model_type: str = None, + order_number: int = None, + origin: str = None, + owner_id: str = None, + provider: str = None, + request_id: str = None, + task: str = None, + user_id: str = None, + workspace_id: str = None, + ): + self.accessibility = accessibility + self.domain = domain + self.extra_info = extra_info + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time + self.labels = labels + self.latest_version = latest_version + self.model_description = model_description + self.model_doc = model_doc + self.model_id = model_id + self.model_name = model_name + self.model_type = model_type + self.order_number = order_number + self.origin = origin + self.owner_id = owner_id + self.provider = provider + self.request_id = request_id + self.task = task + self.user_id = user_id + self.workspace_id = workspace_id + + def validate(self): + if self.labels: + for k in self.labels: + if k: + k.validate() + if self.latest_version: + self.latest_version.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.accessibility is not None: + result['Accessibility'] = self.accessibility + if self.domain is not None: + result['Domain'] = self.domain + if self.extra_info is not None: + result['ExtraInfo'] = self.extra_info + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + if self.latest_version is not None: + result['LatestVersion'] = self.latest_version.to_map() + if self.model_description is not None: + result['ModelDescription'] = self.model_description + if self.model_doc is not None: + result['ModelDoc'] = self.model_doc + if self.model_id is not None: + result['ModelId'] = self.model_id + if self.model_name is not None: + result['ModelName'] = self.model_name + if self.model_type is not None: + result['ModelType'] = self.model_type + if self.order_number is not None: + result['OrderNumber'] = self.order_number + if self.origin is not None: + result['Origin'] = self.origin + if self.owner_id is not None: + result['OwnerId'] = self.owner_id + if self.provider is not None: + result['Provider'] = self.provider + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.task is not None: + result['Task'] = self.task + if self.user_id is not None: + result['UserId'] = self.user_id + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Accessibility') is not None: + self.accessibility = m.get('Accessibility') + if m.get('Domain') is not None: + self.domain = m.get('Domain') + if m.get('ExtraInfo') is not None: + self.extra_info = m.get('ExtraInfo') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = Label() + self.labels.append(temp_model.from_map(k)) + if m.get('LatestVersion') is not None: + temp_model = ModelVersion() + self.latest_version = temp_model.from_map(m['LatestVersion']) + if m.get('ModelDescription') is not None: + self.model_description = m.get('ModelDescription') + if m.get('ModelDoc') is not None: + self.model_doc = m.get('ModelDoc') + if m.get('ModelId') is not None: + self.model_id = m.get('ModelId') + if m.get('ModelName') is not None: + self.model_name = m.get('ModelName') + if m.get('ModelType') is not None: + self.model_type = m.get('ModelType') + if m.get('OrderNumber') is not None: + self.order_number = m.get('OrderNumber') + if m.get('Origin') is not None: + self.origin = m.get('Origin') + if m.get('OwnerId') is not None: + self.owner_id = m.get('OwnerId') + if m.get('Provider') is not None: + self.provider = m.get('Provider') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('Task') is not None: + self.task = m.get('Task') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') + return self + + +class GetModelResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetModelResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetModelResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetModelVersionResponseBody(TeaModel): + def __init__( + self, + approval_status: str = None, + evaluation_spec: Dict[str, Any] = None, + extra_info: Dict[str, Any] = None, + format_type: str = None, + framework_type: str = None, + gmt_create_time: str = None, + gmt_modified_time: str = None, + inference_spec: Dict[str, Any] = None, + labels: List[Label] = None, + metrics: Dict[str, Any] = None, + options: str = None, + owner_id: str = None, + request_id: str = None, + source_id: str = None, + source_type: str = None, + training_spec: Dict[str, Any] = None, + uri: str = None, + user_id: str = None, + version_description: str = None, + version_name: str = None, + ): + self.approval_status = approval_status + self.evaluation_spec = evaluation_spec + self.extra_info = extra_info + self.format_type = format_type + self.framework_type = framework_type + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time + self.inference_spec = inference_spec + self.labels = labels + self.metrics = metrics + self.options = options + self.owner_id = owner_id + self.request_id = request_id + self.source_id = source_id + self.source_type = source_type + self.training_spec = training_spec + self.uri = uri + self.user_id = user_id + self.version_description = version_description + self.version_name = version_name + + def validate(self): + if self.labels: + for k in self.labels: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.approval_status is not None: + result['ApprovalStatus'] = self.approval_status + if self.evaluation_spec is not None: + result['EvaluationSpec'] = self.evaluation_spec + if self.extra_info is not None: + result['ExtraInfo'] = self.extra_info + if self.format_type is not None: + result['FormatType'] = self.format_type + if self.framework_type is not None: + result['FrameworkType'] = self.framework_type + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + if self.inference_spec is not None: + result['InferenceSpec'] = self.inference_spec + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + if self.metrics is not None: + result['Metrics'] = self.metrics + if self.options is not None: + result['Options'] = self.options + if self.owner_id is not None: + result['OwnerId'] = self.owner_id + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.source_id is not None: + result['SourceId'] = self.source_id + if self.source_type is not None: + result['SourceType'] = self.source_type + if self.training_spec is not None: + result['TrainingSpec'] = self.training_spec + if self.uri is not None: + result['Uri'] = self.uri + if self.user_id is not None: + result['UserId'] = self.user_id + if self.version_description is not None: + result['VersionDescription'] = self.version_description + if self.version_name is not None: + result['VersionName'] = self.version_name + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('ApprovalStatus') is not None: + self.approval_status = m.get('ApprovalStatus') + if m.get('EvaluationSpec') is not None: + self.evaluation_spec = m.get('EvaluationSpec') + if m.get('ExtraInfo') is not None: + self.extra_info = m.get('ExtraInfo') + if m.get('FormatType') is not None: + self.format_type = m.get('FormatType') + if m.get('FrameworkType') is not None: + self.framework_type = m.get('FrameworkType') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + if m.get('InferenceSpec') is not None: + self.inference_spec = m.get('InferenceSpec') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = Label() + self.labels.append(temp_model.from_map(k)) + if m.get('Metrics') is not None: + self.metrics = m.get('Metrics') + if m.get('Options') is not None: + self.options = m.get('Options') + if m.get('OwnerId') is not None: + self.owner_id = m.get('OwnerId') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('SourceId') is not None: + self.source_id = m.get('SourceId') + if m.get('SourceType') is not None: + self.source_type = m.get('SourceType') + if m.get('TrainingSpec') is not None: + self.training_spec = m.get('TrainingSpec') + if m.get('Uri') is not None: + self.uri = m.get('Uri') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + if m.get('VersionDescription') is not None: + self.version_description = m.get('VersionDescription') + if m.get('VersionName') is not None: + self.version_name = m.get('VersionName') + return self + + +class GetModelVersionResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetModelVersionResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetModelVersionResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetPayAsYouGoPriceRequestModuleList(TeaModel): + def __init__( + self, + config: str = None, + module_code: str = None, + price_type: str = None, + ): + self.config = config + self.module_code = module_code + self.price_type = price_type + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.config is not None: + result['Config'] = self.config + if self.module_code is not None: + result['ModuleCode'] = self.module_code + if self.price_type is not None: + result['PriceType'] = self.price_type + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Config') is not None: + self.config = m.get('Config') + if m.get('ModuleCode') is not None: + self.module_code = m.get('ModuleCode') + if m.get('PriceType') is not None: + self.price_type = m.get('PriceType') + return self + + +class GetPayAsYouGoPriceRequest(TeaModel): + def __init__( + self, + module_list: GetPayAsYouGoPriceRequestModuleList = None, + product_code: str = None, + product_type: str = None, + subscription_type: str = None, + ): + self.module_list = module_list + self.product_code = product_code + self.product_type = product_type + self.subscription_type = subscription_type + + def validate(self): + if self.module_list: + self.module_list.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.module_list is not None: + result['ModuleList'] = self.module_list.to_map() + if self.product_code is not None: + result['ProductCode'] = self.product_code + if self.product_type is not None: + result['ProductType'] = self.product_type + if self.subscription_type is not None: + result['SubscriptionType'] = self.subscription_type + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('ModuleList') is not None: + temp_model = GetPayAsYouGoPriceRequestModuleList() + self.module_list = temp_model.from_map(m['ModuleList']) + if m.get('ProductCode') is not None: + self.product_code = m.get('ProductCode') + if m.get('ProductType') is not None: + self.product_type = m.get('ProductType') + if m.get('SubscriptionType') is not None: + self.subscription_type = m.get('SubscriptionType') + return self + + +class GetPayAsYouGoPriceResponseBodyModuleDetails(TeaModel): + def __init__( + self, + module_code: str = None, + original_cost: float = None, + unit_price: float = None, + ): + self.module_code = module_code + self.original_cost = original_cost + self.unit_price = unit_price + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.module_code is not None: + result['ModuleCode'] = self.module_code + if self.original_cost is not None: + result['OriginalCost'] = self.original_cost + if self.unit_price is not None: + result['UnitPrice'] = self.unit_price + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('ModuleCode') is not None: + self.module_code = m.get('ModuleCode') + if m.get('OriginalCost') is not None: + self.original_cost = m.get('OriginalCost') + if m.get('UnitPrice') is not None: + self.unit_price = m.get('UnitPrice') + return self + + +class GetPayAsYouGoPriceResponseBody(TeaModel): + def __init__( + self, + currency: str = None, + module_details: List[GetPayAsYouGoPriceResponseBodyModuleDetails] = None, + request_id: str = None, + ): + self.currency = currency + self.module_details = module_details + self.request_id = request_id + + def validate(self): + if self.module_details: + for k in self.module_details: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.currency is not None: + result['Currency'] = self.currency + result['ModuleDetails'] = [] + if self.module_details is not None: + for k in self.module_details: + result['ModuleDetails'].append(k.to_map() if k else None) + if self.request_id is not None: + result['RequestId'] = self.request_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Currency') is not None: + self.currency = m.get('Currency') + self.module_details = [] + if m.get('ModuleDetails') is not None: + for k in m.get('ModuleDetails'): + temp_model = GetPayAsYouGoPriceResponseBodyModuleDetails() + self.module_details.append(temp_model.from_map(k)) + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + return self + + +class GetPayAsYouGoPriceResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetPayAsYouGoPriceResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetPayAsYouGoPriceResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetPermissionRequest(TeaModel): + def __init__( + self, + accessibility: str = None, + creator: str = None, + ): + self.accessibility = accessibility + self.creator = creator + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.accessibility is not None: + result['Accessibility'] = self.accessibility + if self.creator is not None: + result['Creator'] = self.creator + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Accessibility') is not None: + self.accessibility = m.get('Accessibility') + if m.get('Creator') is not None: + self.creator = m.get('Creator') + return self + + +class GetPermissionResponseBodyPermissionRules(TeaModel): + def __init__( + self, + accessibility: str = None, + entity_access_type: str = None, + ): + self.accessibility = accessibility + self.entity_access_type = entity_access_type + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.accessibility is not None: + result['Accessibility'] = self.accessibility + if self.entity_access_type is not None: + result['EntityAccessType'] = self.entity_access_type + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Accessibility') is not None: + self.accessibility = m.get('Accessibility') + if m.get('EntityAccessType') is not None: + self.entity_access_type = m.get('EntityAccessType') + return self + + +class GetPermissionResponseBody(TeaModel): + def __init__( + self, + permission_code: str = None, + permission_rules: List[GetPermissionResponseBodyPermissionRules] = None, + request_id: str = None, + ): + self.permission_code = permission_code + self.permission_rules = permission_rules + self.request_id = request_id + + def validate(self): + if self.permission_rules: + for k in self.permission_rules: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.permission_code is not None: + result['PermissionCode'] = self.permission_code + result['PermissionRules'] = [] + if self.permission_rules is not None: + for k in self.permission_rules: + result['PermissionRules'].append(k.to_map() if k else None) + if self.request_id is not None: + result['RequestId'] = self.request_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('PermissionCode') is not None: + self.permission_code = m.get('PermissionCode') + self.permission_rules = [] + if m.get('PermissionRules') is not None: + for k in m.get('PermissionRules'): + temp_model = GetPermissionResponseBodyPermissionRules() + self.permission_rules.append(temp_model.from_map(k)) + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + return self + + +class GetPermissionResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetPermissionResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetPermissionResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetResourceRequest(TeaModel): + def __init__( + self, + resource_type: str = None, + ): + self.resource_type = resource_type + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.resource_type is not None: + result['ResourceType'] = self.resource_type + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('ResourceType') is not None: + self.resource_type = m.get('ResourceType') + return self + + +class GetResourceResponseBodyQuotasSpecs(TeaModel): + def __init__( + self, + name: str = None, + value: str = None, + ): + self.name = name + self.value = value + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.name is not None: + result['Name'] = self.name + if self.value is not None: + result['Value'] = self.value + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('Value') is not None: + self.value = m.get('Value') + return self + + +class GetResourceResponseBodyQuotas(TeaModel): + def __init__( + self, + card_type: str = None, + display_name: str = None, + id: str = None, + mode: str = None, + name: str = None, + product_code: str = None, + quota_type: str = None, + specs: List[GetResourceResponseBodyQuotasSpecs] = None, + ): + self.card_type = card_type + self.display_name = display_name + self.id = id + self.mode = mode + self.name = name + self.product_code = product_code + self.quota_type = quota_type + self.specs = specs + + def validate(self): + if self.specs: + for k in self.specs: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.card_type is not None: + result['CardType'] = self.card_type + if self.display_name is not None: + result['DisplayName'] = self.display_name + if self.id is not None: + result['Id'] = self.id + if self.mode is not None: + result['Mode'] = self.mode + if self.name is not None: + result['Name'] = self.name + if self.product_code is not None: + result['ProductCode'] = self.product_code + if self.quota_type is not None: + result['QuotaType'] = self.quota_type + result['Specs'] = [] + if self.specs is not None: + for k in self.specs: + result['Specs'].append(k.to_map() if k else None) + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('CardType') is not None: + self.card_type = m.get('CardType') + if m.get('DisplayName') is not None: + self.display_name = m.get('DisplayName') + if m.get('Id') is not None: + self.id = m.get('Id') + if m.get('Mode') is not None: + self.mode = m.get('Mode') + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('ProductCode') is not None: + self.product_code = m.get('ProductCode') + if m.get('QuotaType') is not None: + self.quota_type = m.get('QuotaType') + self.specs = [] + if m.get('Specs') is not None: + for k in m.get('Specs'): + temp_model = GetResourceResponseBodyQuotasSpecs() + self.specs.append(temp_model.from_map(k)) + return self + + +class GetResourceResponseBody(TeaModel): + def __init__( + self, + env_type: str = None, + gmt_create_time: str = None, + group_name: str = None, + id: str = None, + is_default: bool = None, + name: str = None, + quotas: List[GetResourceResponseBodyQuotas] = None, + request_id: str = None, + resource_type: str = None, + spec: Dict[str, Any] = None, + status: str = None, + workspace_id: str = None, + ): + self.env_type = env_type + self.gmt_create_time = gmt_create_time + self.group_name = group_name + self.id = id + self.is_default = is_default + self.name = name + self.quotas = quotas + self.request_id = request_id + self.resource_type = resource_type + self.spec = spec + self.status = status + self.workspace_id = workspace_id + + def validate(self): + if self.quotas: + for k in self.quotas: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.env_type is not None: + result['EnvType'] = self.env_type + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.group_name is not None: + result['GroupName'] = self.group_name + if self.id is not None: + result['Id'] = self.id + if self.is_default is not None: + result['IsDefault'] = self.is_default + if self.name is not None: + result['Name'] = self.name + result['Quotas'] = [] + if self.quotas is not None: + for k in self.quotas: + result['Quotas'].append(k.to_map() if k else None) + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.resource_type is not None: + result['ResourceType'] = self.resource_type + if self.spec is not None: + result['Spec'] = self.spec + if self.status is not None: + result['Status'] = self.status + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('EnvType') is not None: + self.env_type = m.get('EnvType') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GroupName') is not None: + self.group_name = m.get('GroupName') + if m.get('Id') is not None: + self.id = m.get('Id') + if m.get('IsDefault') is not None: + self.is_default = m.get('IsDefault') + if m.get('Name') is not None: + self.name = m.get('Name') + self.quotas = [] + if m.get('Quotas') is not None: + for k in m.get('Quotas'): + temp_model = GetResourceResponseBodyQuotas() + self.quotas.append(temp_model.from_map(k)) + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('ResourceType') is not None: + self.resource_type = m.get('ResourceType') + if m.get('Spec') is not None: + self.spec = m.get('Spec') + if m.get('Status') is not None: + self.status = m.get('Status') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') + return self + + +class GetResourceResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetResourceResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetResourceResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetRoleStatisticsRequest(TeaModel): + def __init__( + self, + workspace_id: str = None, + ): + self.workspace_id = workspace_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') + return self + + +class GetRoleStatisticsResponseBodyRoles(TeaModel): + def __init__( + self, + member_size: int = None, + role_name: str = None, + ): + self.member_size = member_size + self.role_name = role_name + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.member_size is not None: + result['MemberSize'] = self.member_size + if self.role_name is not None: + result['RoleName'] = self.role_name + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('MemberSize') is not None: + self.member_size = m.get('MemberSize') + if m.get('RoleName') is not None: + self.role_name = m.get('RoleName') + return self + + +class GetRoleStatisticsResponseBody(TeaModel): + def __init__( + self, + request_id: str = None, + roles: List[GetRoleStatisticsResponseBodyRoles] = None, + total_count: int = None, + ): + self.request_id = request_id + self.roles = roles + self.total_count = total_count + + def validate(self): + if self.roles: + for k in self.roles: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.request_id is not None: + result['RequestId'] = self.request_id + result['Roles'] = [] + if self.roles is not None: + for k in self.roles: + result['Roles'].append(k.to_map() if k else None) + if self.total_count is not None: + result['TotalCount'] = self.total_count + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + self.roles = [] + if m.get('Roles') is not None: + for k in m.get('Roles'): + temp_model = GetRoleStatisticsResponseBodyRoles() + self.roles.append(temp_model.from_map(k)) + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') + return self + + +class GetRoleStatisticsResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetRoleStatisticsResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetRoleStatisticsResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetServiceTemplateResponseBody(TeaModel): + def __init__( + self, + gmt_create_time: str = None, + gmt_modified_time: str = None, + inference_spec: Dict[str, Any] = None, + labels: List[Label] = None, + owner_id: str = None, + provider: str = None, + request_id: str = None, + service_template_description: str = None, + service_template_doc: str = None, + service_template_id: str = None, + service_template_name: str = None, + user_id: str = None, + ): + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time + self.inference_spec = inference_spec + self.labels = labels + self.owner_id = owner_id + self.provider = provider + self.request_id = request_id + self.service_template_description = service_template_description + self.service_template_doc = service_template_doc + self.service_template_id = service_template_id + self.service_template_name = service_template_name + self.user_id = user_id + + def validate(self): + if self.labels: + for k in self.labels: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + if self.inference_spec is not None: + result['InferenceSpec'] = self.inference_spec + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + if self.owner_id is not None: + result['OwnerId'] = self.owner_id + if self.provider is not None: + result['Provider'] = self.provider + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.service_template_description is not None: + result['ServiceTemplateDescription'] = self.service_template_description + if self.service_template_doc is not None: + result['ServiceTemplateDoc'] = self.service_template_doc + if self.service_template_id is not None: + result['ServiceTemplateId'] = self.service_template_id + if self.service_template_name is not None: + result['ServiceTemplateName'] = self.service_template_name + if self.user_id is not None: + result['UserId'] = self.user_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + if m.get('InferenceSpec') is not None: + self.inference_spec = m.get('InferenceSpec') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = Label() + self.labels.append(temp_model.from_map(k)) + if m.get('OwnerId') is not None: + self.owner_id = m.get('OwnerId') + if m.get('Provider') is not None: + self.provider = m.get('Provider') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('ServiceTemplateDescription') is not None: + self.service_template_description = m.get('ServiceTemplateDescription') + if m.get('ServiceTemplateDoc') is not None: + self.service_template_doc = m.get('ServiceTemplateDoc') + if m.get('ServiceTemplateId') is not None: + self.service_template_id = m.get('ServiceTemplateId') + if m.get('ServiceTemplateName') is not None: + self.service_template_name = m.get('ServiceTemplateName') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + return self + + +class GetServiceTemplateResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetServiceTemplateResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetServiceTemplateResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetTrialResponseBody(TeaModel): + def __init__( + self, + accessibility: str = None, + experiment_id: str = None, + gmt_create_time: str = None, + gmt_modified_time: str = None, + name: str = None, + request_id: str = None, + source_id: str = None, + source_type: str = None, + trial_id: str = None, + workspace_id: str = None, + ): + self.accessibility = accessibility + self.experiment_id = experiment_id + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time + self.name = name + self.request_id = request_id + self.source_id = source_id + self.source_type = source_type + self.trial_id = trial_id + self.workspace_id = workspace_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.accessibility is not None: + result['Accessibility'] = self.accessibility + if self.experiment_id is not None: + result['ExperimentId'] = self.experiment_id + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + if self.name is not None: + result['Name'] = self.name + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.source_id is not None: + result['SourceId'] = self.source_id + if self.source_type is not None: + result['SourceType'] = self.source_type + if self.trial_id is not None: + result['TrialId'] = self.trial_id + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Accessibility') is not None: + self.accessibility = m.get('Accessibility') + if m.get('ExperimentId') is not None: + self.experiment_id = m.get('ExperimentId') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('SourceId') is not None: + self.source_id = m.get('SourceId') + if m.get('SourceType') is not None: + self.source_type = m.get('SourceType') + if m.get('TrialId') is not None: + self.trial_id = m.get('TrialId') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') + return self + + +class GetTrialResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetTrialResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetTrialResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetWorkspaceRequest(TeaModel): + def __init__( + self, + verbose: bool = None, + ): + self.verbose = verbose + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.verbose is not None: + result['Verbose'] = self.verbose + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Verbose') is not None: + self.verbose = m.get('Verbose') + return self + + +class GetWorkspaceResponseBodyOwner(TeaModel): + def __init__( + self, + display_name: str = None, + user_id: str = None, + user_kp: str = None, + user_name: str = None, + ): + self.display_name = display_name + self.user_id = user_id + self.user_kp = user_kp + self.user_name = user_name + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.display_name is not None: + result['DisplayName'] = self.display_name + if self.user_id is not None: + result['UserId'] = self.user_id + if self.user_kp is not None: + result['UserKp'] = self.user_kp + if self.user_name is not None: + result['UserName'] = self.user_name + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('DisplayName') is not None: + self.display_name = m.get('DisplayName') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + if m.get('UserKp') is not None: + self.user_kp = m.get('UserKp') + if m.get('UserName') is not None: + self.user_name = m.get('UserName') + return self + + +class GetWorkspaceResponseBody(TeaModel): + def __init__( + self, + admin_names: List[str] = None, + creator: str = None, + description: str = None, + display_name: str = None, + env_types: List[str] = None, + extra_infos: Dict[str, Any] = None, + gmt_create_time: str = None, + gmt_modified_time: str = None, + is_default: bool = None, + owner: GetWorkspaceResponseBodyOwner = None, + request_id: str = None, + status: str = None, + workspace_id: str = None, + workspace_name: str = None, + ): + self.admin_names = admin_names + self.creator = creator + self.description = description + self.display_name = display_name + self.env_types = env_types + self.extra_infos = extra_infos + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time + self.is_default = is_default + self.owner = owner + self.request_id = request_id + self.status = status + self.workspace_id = workspace_id + self.workspace_name = workspace_name + + def validate(self): + if self.owner: + self.owner.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.admin_names is not None: + result['AdminNames'] = self.admin_names + if self.creator is not None: + result['Creator'] = self.creator + if self.description is not None: + result['Description'] = self.description + if self.display_name is not None: + result['DisplayName'] = self.display_name + if self.env_types is not None: + result['EnvTypes'] = self.env_types + if self.extra_infos is not None: + result['ExtraInfos'] = self.extra_infos + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + if self.is_default is not None: + result['IsDefault'] = self.is_default + if self.owner is not None: + result['Owner'] = self.owner.to_map() + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.status is not None: + result['Status'] = self.status + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id + if self.workspace_name is not None: + result['WorkspaceName'] = self.workspace_name + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('AdminNames') is not None: + self.admin_names = m.get('AdminNames') + if m.get('Creator') is not None: + self.creator = m.get('Creator') + if m.get('Description') is not None: + self.description = m.get('Description') + if m.get('DisplayName') is not None: + self.display_name = m.get('DisplayName') + if m.get('EnvTypes') is not None: + self.env_types = m.get('EnvTypes') + if m.get('ExtraInfos') is not None: + self.extra_infos = m.get('ExtraInfos') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + if m.get('IsDefault') is not None: + self.is_default = m.get('IsDefault') + if m.get('Owner') is not None: + temp_model = GetWorkspaceResponseBodyOwner() + self.owner = temp_model.from_map(m['Owner']) + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('Status') is not None: + self.status = m.get('Status') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') + if m.get('WorkspaceName') is not None: + self.workspace_name = m.get('WorkspaceName') + return self + + +class GetWorkspaceResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetWorkspaceResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetWorkspaceResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class ListCodeSourcesRequest(TeaModel): + def __init__( + self, + display_name: str = None, + order: str = None, + page_number: int = None, + page_size: int = None, + sort_by: str = None, + workspace_id: str = None, + ): + self.display_name = display_name + self.order = order + self.page_number = page_number + self.page_size = page_size + self.sort_by = sort_by + self.workspace_id = workspace_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.display_name is not None: + result['DisplayName'] = self.display_name + if self.order is not None: + result['Order'] = self.order + if self.page_number is not None: + result['PageNumber'] = self.page_number + if self.page_size is not None: + result['PageSize'] = self.page_size + if self.sort_by is not None: + result['SortBy'] = self.sort_by + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('DisplayName') is not None: + self.display_name = m.get('DisplayName') + if m.get('Order') is not None: + self.order = m.get('Order') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + if m.get('SortBy') is not None: + self.sort_by = m.get('SortBy') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') + return self + + +class ListCodeSourcesResponseBody(TeaModel): + def __init__( + self, + code_sources: List[CodeSourceItem] = None, + request_id: str = None, + total_count: int = None, + ): + self.code_sources = code_sources + self.request_id = request_id + self.total_count = total_count + + def validate(self): + if self.code_sources: + for k in self.code_sources: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + result['CodeSources'] = [] + if self.code_sources is not None: + for k in self.code_sources: + result['CodeSources'].append(k.to_map() if k else None) + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.total_count is not None: + result['TotalCount'] = self.total_count + return result + + def from_map(self, m: dict = None): + m = m or dict() + self.code_sources = [] + if m.get('CodeSources') is not None: + for k in m.get('CodeSources'): + temp_model = CodeSourceItem() + self.code_sources.append(temp_model.from_map(k)) + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') + return self + + +class ListCodeSourcesResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: ListCodeSourcesResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = ListCodeSourcesResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class ListCollectionsRequest(TeaModel): + def __init__( + self, + page_number: int = None, + page_size: int = None, + ): + self.page_number = page_number + self.page_size = page_size + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.page_number is not None: + result['PageNumber'] = self.page_number + if self.page_size is not None: + result['PageSize'] = self.page_size + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + return self + + +class ListCollectionsResponseBody(TeaModel): + def __init__( + self, + collections: List[Collection] = None, + request_id: str = None, + total_count: int = None, + ): + self.collections = collections + self.request_id = request_id + self.total_count = total_count + + def validate(self): + if self.collections: + for k in self.collections: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + result['Collections'] = [] + if self.collections is not None: + for k in self.collections: + result['Collections'].append(k.to_map() if k else None) + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.total_count is not None: + result['TotalCount'] = self.total_count + return result + + def from_map(self, m: dict = None): + m = m or dict() + self.collections = [] + if m.get('Collections') is not None: + for k in m.get('Collections'): + temp_model = Collection() + self.collections.append(temp_model.from_map(k)) + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') + return self + + +class ListCollectionsResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: ListCollectionsResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = ListCollectionsResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class ListConfigsRequest(TeaModel): + def __init__( + self, + config_keys: str = None, + labels: str = None, + verbose: str = None, + ): + self.config_keys = config_keys + self.labels = labels + self.verbose = verbose + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.config_keys is not None: + result['ConfigKeys'] = self.config_keys + if self.labels is not None: + result['Labels'] = self.labels + if self.verbose is not None: + result['Verbose'] = self.verbose + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('ConfigKeys') is not None: + self.config_keys = m.get('ConfigKeys') + if m.get('Labels') is not None: + self.labels = m.get('Labels') + if m.get('Verbose') is not None: + self.verbose = m.get('Verbose') + return self + + +class ListConfigsResponseBodyConfigsLabels(TeaModel): + def __init__( + self, + key: str = None, + value: str = None, + ): + self.key = key + self.value = value + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.key is not None: + result['Key'] = self.key + if self.value is not None: + result['Value'] = self.value + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Key') is not None: + self.key = m.get('Key') + if m.get('Value') is not None: + self.value = m.get('Value') + return self + + +class ListConfigsResponseBodyConfigs(TeaModel): + def __init__( + self, + config_key: str = None, + config_value: str = None, + labels: List[ListConfigsResponseBodyConfigsLabels] = None, + ): + self.config_key = config_key + self.config_value = config_value self.labels = labels - self.latest_version = latest_version - self.model_description = model_description - self.model_doc = model_doc - self.model_id = model_id - self.model_name = model_name - self.origin = origin - self.owner_id = owner_id - self.provider = provider - self.request_id = request_id - self.task = task - self.user_id = user_id - self.workspace_id = workspace_id def validate(self): if self.labels: for k in self.labels: if k: k.validate() - if self.latest_version: - self.latest_version.validate() def to_map(self): _map = super().to_map() @@ -5987,102 +10087,89 @@ def to_map(self): return _map result = dict() - if self.accessibility is not None: - result['Accessibility'] = self.accessibility - if self.domain is not None: - result['Domain'] = self.domain - if self.gmt_create_time is not None: - result['GmtCreateTime'] = self.gmt_create_time - if self.gmt_modified_time is not None: - result['GmtModifiedTime'] = self.gmt_modified_time + if self.config_key is not None: + result['ConfigKey'] = self.config_key + if self.config_value is not None: + result['ConfigValue'] = self.config_value result['Labels'] = [] if self.labels is not None: for k in self.labels: result['Labels'].append(k.to_map() if k else None) - if self.latest_version is not None: - result['LatestVersion'] = self.latest_version.to_map() - if self.model_description is not None: - result['ModelDescription'] = self.model_description - if self.model_doc is not None: - result['ModelDoc'] = self.model_doc - if self.model_id is not None: - result['ModelId'] = self.model_id - if self.model_name is not None: - result['ModelName'] = self.model_name - if self.origin is not None: - result['Origin'] = self.origin - if self.owner_id is not None: - result['OwnerId'] = self.owner_id - if self.provider is not None: - result['Provider'] = self.provider - if self.request_id is not None: - result['RequestId'] = self.request_id - if self.task is not None: - result['Task'] = self.task - if self.user_id is not None: - result['UserId'] = self.user_id - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Accessibility') is not None: - self.accessibility = m.get('Accessibility') - if m.get('Domain') is not None: - self.domain = m.get('Domain') - if m.get('GmtCreateTime') is not None: - self.gmt_create_time = m.get('GmtCreateTime') - if m.get('GmtModifiedTime') is not None: - self.gmt_modified_time = m.get('GmtModifiedTime') + if m.get('ConfigKey') is not None: + self.config_key = m.get('ConfigKey') + if m.get('ConfigValue') is not None: + self.config_value = m.get('ConfigValue') self.labels = [] if m.get('Labels') is not None: for k in m.get('Labels'): - temp_model = Label() + temp_model = ListConfigsResponseBodyConfigsLabels() self.labels.append(temp_model.from_map(k)) - if m.get('LatestVersion') is not None: - temp_model = ModelVersion() - self.latest_version = temp_model.from_map(m['LatestVersion']) - if m.get('ModelDescription') is not None: - self.model_description = m.get('ModelDescription') - if m.get('ModelDoc') is not None: - self.model_doc = m.get('ModelDoc') - if m.get('ModelId') is not None: - self.model_id = m.get('ModelId') - if m.get('ModelName') is not None: - self.model_name = m.get('ModelName') - if m.get('Origin') is not None: - self.origin = m.get('Origin') - if m.get('OwnerId') is not None: - self.owner_id = m.get('OwnerId') - if m.get('Provider') is not None: - self.provider = m.get('Provider') - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - if m.get('Task') is not None: - self.task = m.get('Task') - if m.get('UserId') is not None: - self.user_id = m.get('UserId') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') return self -class GetModelResponse(TeaModel): +class ListConfigsResponseBody(TeaModel): + def __init__( + self, + configs: List[ListConfigsResponseBodyConfigs] = None, + request_id: str = None, + total_count: int = None, + ): + self.configs = configs + self.request_id = request_id + self.total_count = total_count + + def validate(self): + if self.configs: + for k in self.configs: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + result['Configs'] = [] + if self.configs is not None: + for k in self.configs: + result['Configs'].append(k.to_map() if k else None) + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.total_count is not None: + result['TotalCount'] = self.total_count + return result + + def from_map(self, m: dict = None): + m = m or dict() + self.configs = [] + if m.get('Configs') is not None: + for k in m.get('Configs'): + temp_model = ListConfigsResponseBodyConfigs() + self.configs.append(temp_model.from_map(k)) + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') + return self + + +class ListConfigsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetModelResponseBody = None, + body: ListConfigsResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -6107,55 +10194,46 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetModelResponseBody() + temp_model = ListConfigsResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetModelVersionResponseBody(TeaModel): +class ListDatasetsRequest(TeaModel): def __init__( self, - approval_status: str = None, - format_type: str = None, - framework_type: str = None, - gmt_create_time: str = None, - gmt_modified_time: str = None, - inference_spec: Dict[str, Any] = None, - labels: List[Label] = None, - options: str = None, - owner_id: str = None, - request_id: str = None, + data_source_types: str = None, + data_types: str = None, + label: str = None, + label_keys: str = None, + label_values: str = None, + name: str = None, + order: str = None, + page_number: int = None, + page_size: int = None, + properties: str = None, + provider: str = None, source_id: str = None, - source_type: str = None, - training_spec: Dict[str, Any] = None, - uri: str = None, - user_id: str = None, - version_description: str = None, - version_name: str = None, + source_types: str = None, + workspace_id: str = None, ): - self.approval_status = approval_status - self.format_type = format_type - self.framework_type = framework_type - self.gmt_create_time = gmt_create_time - self.gmt_modified_time = gmt_modified_time - self.inference_spec = inference_spec - self.labels = labels - self.options = options - self.owner_id = owner_id - self.request_id = request_id + self.data_source_types = data_source_types + self.data_types = data_types + self.label = label + self.label_keys = label_keys + self.label_values = label_values + self.name = name + self.order = order + self.page_number = page_number + self.page_size = page_size + self.properties = properties + self.provider = provider self.source_id = source_id - self.source_type = source_type - self.training_spec = training_spec - self.uri = uri - self.user_id = user_id - self.version_description = version_description - self.version_name = version_name + self.source_types = source_types + self.workspace_id = workspace_id def validate(self): - if self.labels: - for k in self.labels: - if k: - k.validate() + pass def to_map(self): _map = super().to_map() @@ -6163,103 +10241,85 @@ def to_map(self): return _map result = dict() - if self.approval_status is not None: - result['ApprovalStatus'] = self.approval_status - if self.format_type is not None: - result['FormatType'] = self.format_type - if self.framework_type is not None: - result['FrameworkType'] = self.framework_type - if self.gmt_create_time is not None: - result['GmtCreateTime'] = self.gmt_create_time - if self.gmt_modified_time is not None: - result['GmtModifiedTime'] = self.gmt_modified_time - if self.inference_spec is not None: - result['InferenceSpec'] = self.inference_spec - result['Labels'] = [] - if self.labels is not None: - for k in self.labels: - result['Labels'].append(k.to_map() if k else None) - if self.options is not None: - result['Options'] = self.options - if self.owner_id is not None: - result['OwnerId'] = self.owner_id - if self.request_id is not None: - result['RequestId'] = self.request_id + if self.data_source_types is not None: + result['DataSourceTypes'] = self.data_source_types + if self.data_types is not None: + result['DataTypes'] = self.data_types + if self.label is not None: + result['Label'] = self.label + if self.label_keys is not None: + result['LabelKeys'] = self.label_keys + if self.label_values is not None: + result['LabelValues'] = self.label_values + if self.name is not None: + result['Name'] = self.name + if self.order is not None: + result['Order'] = self.order + if self.page_number is not None: + result['PageNumber'] = self.page_number + if self.page_size is not None: + result['PageSize'] = self.page_size + if self.properties is not None: + result['Properties'] = self.properties + if self.provider is not None: + result['Provider'] = self.provider if self.source_id is not None: result['SourceId'] = self.source_id - if self.source_type is not None: - result['SourceType'] = self.source_type - if self.training_spec is not None: - result['TrainingSpec'] = self.training_spec - if self.uri is not None: - result['Uri'] = self.uri - if self.user_id is not None: - result['UserId'] = self.user_id - if self.version_description is not None: - result['VersionDescription'] = self.version_description - if self.version_name is not None: - result['VersionName'] = self.version_name + if self.source_types is not None: + result['SourceTypes'] = self.source_types + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ApprovalStatus') is not None: - self.approval_status = m.get('ApprovalStatus') - if m.get('FormatType') is not None: - self.format_type = m.get('FormatType') - if m.get('FrameworkType') is not None: - self.framework_type = m.get('FrameworkType') - if m.get('GmtCreateTime') is not None: - self.gmt_create_time = m.get('GmtCreateTime') - if m.get('GmtModifiedTime') is not None: - self.gmt_modified_time = m.get('GmtModifiedTime') - if m.get('InferenceSpec') is not None: - self.inference_spec = m.get('InferenceSpec') - self.labels = [] - if m.get('Labels') is not None: - for k in m.get('Labels'): - temp_model = Label() - self.labels.append(temp_model.from_map(k)) - if m.get('Options') is not None: - self.options = m.get('Options') - if m.get('OwnerId') is not None: - self.owner_id = m.get('OwnerId') - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') + if m.get('DataSourceTypes') is not None: + self.data_source_types = m.get('DataSourceTypes') + if m.get('DataTypes') is not None: + self.data_types = m.get('DataTypes') + if m.get('Label') is not None: + self.label = m.get('Label') + if m.get('LabelKeys') is not None: + self.label_keys = m.get('LabelKeys') + if m.get('LabelValues') is not None: + self.label_values = m.get('LabelValues') + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('Order') is not None: + self.order = m.get('Order') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + if m.get('Properties') is not None: + self.properties = m.get('Properties') + if m.get('Provider') is not None: + self.provider = m.get('Provider') if m.get('SourceId') is not None: self.source_id = m.get('SourceId') - if m.get('SourceType') is not None: - self.source_type = m.get('SourceType') - if m.get('TrainingSpec') is not None: - self.training_spec = m.get('TrainingSpec') - if m.get('Uri') is not None: - self.uri = m.get('Uri') - if m.get('UserId') is not None: - self.user_id = m.get('UserId') - if m.get('VersionDescription') is not None: - self.version_description = m.get('VersionDescription') - if m.get('VersionName') is not None: - self.version_name = m.get('VersionName') + if m.get('SourceTypes') is not None: + self.source_types = m.get('SourceTypes') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class GetModelVersionResponse(TeaModel): +class ListDatasetsResponseBody(TeaModel): def __init__( self, - headers: Dict[str, str] = None, - status_code: int = None, - body: GetModelVersionResponseBody = None, + datasets: List[Dataset] = None, + request_id: str = None, + total_count: int = None, ): - self.headers = headers - self.status_code = status_code - self.body = body + self.datasets = datasets + self.request_id = request_id + self.total_count = total_count def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') - if self.body: - self.body.validate() + if self.datasets: + for k in self.datasets: + if k: + k.validate() def to_map(self): _map = super().to_map() @@ -6267,37 +10327,44 @@ def to_map(self): return _map result = dict() - if self.headers is not None: - result['headers'] = self.headers - if self.status_code is not None: - result['statusCode'] = self.status_code - if self.body is not None: - result['body'] = self.body.to_map() + result['Datasets'] = [] + if self.datasets is not None: + for k in self.datasets: + result['Datasets'].append(k.to_map() if k else None) + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.total_count is not None: + result['TotalCount'] = self.total_count return result def from_map(self, m: dict = None): m = m or dict() - if m.get('headers') is not None: - self.headers = m.get('headers') - if m.get('statusCode') is not None: - self.status_code = m.get('statusCode') - if m.get('body') is not None: - temp_model = GetModelVersionResponseBody() - self.body = temp_model.from_map(m['body']) + self.datasets = [] + if m.get('Datasets') is not None: + for k in m.get('Datasets'): + temp_model = Dataset() + self.datasets.append(temp_model.from_map(k)) + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') return self -class GetPermissionRequest(TeaModel): +class ListDatasetsResponse(TeaModel): def __init__( self, - accessibility: str = None, - creator: str = None, + headers: Dict[str, str] = None, + status_code: int = None, + body: ListDatasetsResponseBody = None, ): - self.accessibility = accessibility - self.creator = creator + self.headers = headers + self.status_code = status_code + self.body = body def validate(self): - pass + if self.body: + self.body.validate() def to_map(self): _map = super().to_map() @@ -6305,29 +10372,44 @@ def to_map(self): return _map result = dict() - if self.accessibility is not None: - result['Accessibility'] = self.accessibility - if self.creator is not None: - result['Creator'] = self.creator + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Accessibility') is not None: - self.accessibility = m.get('Accessibility') - if m.get('Creator') is not None: - self.creator = m.get('Creator') + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = ListDatasetsResponseBody() + self.body = temp_model.from_map(m['body']) return self -class GetPermissionResponseBodyPermissionRules(TeaModel): +class ListExperimentRequest(TeaModel): def __init__( self, - accessibility: str = None, - entity_access_type: str = None, + labels: str = None, + name: str = None, + order: str = None, + page_number: int = None, + page_size: int = None, + sort_by: str = None, + workspace_id: str = None, ): - self.accessibility = accessibility - self.entity_access_type = entity_access_type + self.labels = labels + self.name = name + self.order = order + self.page_number = page_number + self.page_size = page_size + self.sort_by = sort_by + self.workspace_id = workspace_id def validate(self): pass @@ -6338,35 +10420,55 @@ def to_map(self): return _map result = dict() - if self.accessibility is not None: - result['Accessibility'] = self.accessibility - if self.entity_access_type is not None: - result['EntityAccessType'] = self.entity_access_type + if self.labels is not None: + result['Labels'] = self.labels + if self.name is not None: + result['Name'] = self.name + if self.order is not None: + result['Order'] = self.order + if self.page_number is not None: + result['PageNumber'] = self.page_number + if self.page_size is not None: + result['PageSize'] = self.page_size + if self.sort_by is not None: + result['SortBy'] = self.sort_by + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Accessibility') is not None: - self.accessibility = m.get('Accessibility') - if m.get('EntityAccessType') is not None: - self.entity_access_type = m.get('EntityAccessType') + if m.get('Labels') is not None: + self.labels = m.get('Labels') + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('Order') is not None: + self.order = m.get('Order') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + if m.get('SortBy') is not None: + self.sort_by = m.get('SortBy') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class GetPermissionResponseBody(TeaModel): +class ListExperimentResponseBody(TeaModel): def __init__( self, - permission_code: str = None, - permission_rules: List[GetPermissionResponseBodyPermissionRules] = None, + experiments: List[Experiment] = None, + total_count: int = None, request_id: str = None, ): - self.permission_code = permission_code - self.permission_rules = permission_rules + self.experiments = experiments + self.total_count = total_count self.request_id = request_id def validate(self): - if self.permission_rules: - for k in self.permission_rules: + if self.experiments: + for k in self.experiments: if k: k.validate() @@ -6376,45 +10478,42 @@ def to_map(self): return _map result = dict() - if self.permission_code is not None: - result['PermissionCode'] = self.permission_code - result['PermissionRules'] = [] - if self.permission_rules is not None: - for k in self.permission_rules: - result['PermissionRules'].append(k.to_map() if k else None) + result['Experiments'] = [] + if self.experiments is not None: + for k in self.experiments: + result['Experiments'].append(k.to_map() if k else None) + if self.total_count is not None: + result['TotalCount'] = self.total_count if self.request_id is not None: - result['RequestId'] = self.request_id + result['requestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('PermissionCode') is not None: - self.permission_code = m.get('PermissionCode') - self.permission_rules = [] - if m.get('PermissionRules') is not None: - for k in m.get('PermissionRules'): - temp_model = GetPermissionResponseBodyPermissionRules() - self.permission_rules.append(temp_model.from_map(k)) - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') + self.experiments = [] + if m.get('Experiments') is not None: + for k in m.get('Experiments'): + temp_model = Experiment() + self.experiments.append(temp_model.from_map(k)) + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') + if m.get('requestId') is not None: + self.request_id = m.get('requestId') return self -class GetPermissionResponse(TeaModel): +class ListExperimentResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetPermissionResponseBody = None, + body: ListExperimentResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -6439,46 +10538,17 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetPermissionResponseBody() + temp_model = ListExperimentResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetRoleStatisticsRequest(TeaModel): - def __init__( - self, - workspace_id: str = None, - ): - self.workspace_id = workspace_id - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') - return self - - -class GetRoleStatisticsResponseBodyRoles(TeaModel): +class ListFeaturesRequest(TeaModel): def __init__( self, - member_size: int = None, - role_name: str = None, + names: str = None, ): - self.member_size = member_size - self.role_name = role_name + self.names = names def validate(self): pass @@ -6489,37 +10559,30 @@ def to_map(self): return _map result = dict() - if self.member_size is not None: - result['MemberSize'] = self.member_size - if self.role_name is not None: - result['RoleName'] = self.role_name + if self.names is not None: + result['Names'] = self.names return result def from_map(self, m: dict = None): m = m or dict() - if m.get('MemberSize') is not None: - self.member_size = m.get('MemberSize') - if m.get('RoleName') is not None: - self.role_name = m.get('RoleName') + if m.get('Names') is not None: + self.names = m.get('Names') return self -class GetRoleStatisticsResponseBody(TeaModel): +class ListFeaturesResponseBody(TeaModel): def __init__( self, + features: List[str] = None, request_id: str = None, - roles: List[GetRoleStatisticsResponseBodyRoles] = None, total_count: int = None, ): + self.features = features self.request_id = request_id - self.roles = roles self.total_count = total_count def validate(self): - if self.roles: - for k in self.roles: - if k: - k.validate() + pass def to_map(self): _map = super().to_map() @@ -6527,45 +10590,37 @@ def to_map(self): return _map result = dict() + if self.features is not None: + result['Features'] = self.features if self.request_id is not None: result['RequestId'] = self.request_id - result['Roles'] = [] - if self.roles is not None: - for k in self.roles: - result['Roles'].append(k.to_map() if k else None) if self.total_count is not None: result['TotalCount'] = self.total_count return result def from_map(self, m: dict = None): m = m or dict() + if m.get('Features') is not None: + self.features = m.get('Features') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - self.roles = [] - if m.get('Roles') is not None: - for k in m.get('Roles'): - temp_model = GetRoleStatisticsResponseBodyRoles() - self.roles.append(temp_model.from_map(k)) if m.get('TotalCount') is not None: self.total_count = m.get('TotalCount') return self -class GetRoleStatisticsResponse(TeaModel): +class ListFeaturesResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetRoleStatisticsResponseBody = None, + body: ListFeaturesResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -6590,17 +10645,19 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetRoleStatisticsResponseBody() + temp_model = ListFeaturesResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetWorkspaceRequest(TeaModel): +class ListGlobalPermissionsResponseBodyPermissionsPermissionRules(TeaModel): def __init__( self, - verbose: bool = None, + accessibility: str = None, + entity_access_type: str = None, ): - self.verbose = verbose + self.accessibility = accessibility + self.entity_access_type = entity_access_type def validate(self): pass @@ -6611,32 +10668,35 @@ def to_map(self): return _map result = dict() - if self.verbose is not None: - result['Verbose'] = self.verbose + if self.accessibility is not None: + result['Accessibility'] = self.accessibility + if self.entity_access_type is not None: + result['EntityAccessType'] = self.entity_access_type return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Verbose') is not None: - self.verbose = m.get('Verbose') + if m.get('Accessibility') is not None: + self.accessibility = m.get('Accessibility') + if m.get('EntityAccessType') is not None: + self.entity_access_type = m.get('EntityAccessType') return self -class GetWorkspaceResponseBodyOwner(TeaModel): +class ListGlobalPermissionsResponseBodyPermissions(TeaModel): def __init__( self, - display_name: str = None, - user_id: str = None, - user_kp: str = None, - user_name: str = None, + permission_code: str = None, + permission_rules: List[ListGlobalPermissionsResponseBodyPermissionsPermissionRules] = None, ): - self.display_name = display_name - self.user_id = user_id - self.user_kp = user_kp - self.user_name = user_name + self.permission_code = permission_code + self.permission_rules = permission_rules def validate(self): - pass + if self.permission_rules: + for k in self.permission_rules: + if k: + k.validate() def to_map(self): _map = super().to_map() @@ -6644,65 +10704,40 @@ def to_map(self): return _map result = dict() - if self.display_name is not None: - result['DisplayName'] = self.display_name - if self.user_id is not None: - result['UserId'] = self.user_id - if self.user_kp is not None: - result['UserKp'] = self.user_kp - if self.user_name is not None: - result['UserName'] = self.user_name + if self.permission_code is not None: + result['PermissionCode'] = self.permission_code + result['PermissionRules'] = [] + if self.permission_rules is not None: + for k in self.permission_rules: + result['PermissionRules'].append(k.to_map() if k else None) return result def from_map(self, m: dict = None): - m = m or dict() - if m.get('DisplayName') is not None: - self.display_name = m.get('DisplayName') - if m.get('UserId') is not None: - self.user_id = m.get('UserId') - if m.get('UserKp') is not None: - self.user_kp = m.get('UserKp') - if m.get('UserName') is not None: - self.user_name = m.get('UserName') + m = m or dict() + if m.get('PermissionCode') is not None: + self.permission_code = m.get('PermissionCode') + self.permission_rules = [] + if m.get('PermissionRules') is not None: + for k in m.get('PermissionRules'): + temp_model = ListGlobalPermissionsResponseBodyPermissionsPermissionRules() + self.permission_rules.append(temp_model.from_map(k)) return self -class GetWorkspaceResponseBody(TeaModel): +class ListGlobalPermissionsResponseBody(TeaModel): def __init__( self, - admin_names: List[str] = None, - creator: str = None, - description: str = None, - display_name: str = None, - env_types: List[str] = None, - extra_infos: Dict[str, Any] = None, - gmt_create_time: str = None, - gmt_modified_time: str = None, - is_default: bool = None, - owner: GetWorkspaceResponseBodyOwner = None, + permissions: List[ListGlobalPermissionsResponseBodyPermissions] = None, request_id: str = None, - status: str = None, - workspace_id: str = None, - workspace_name: str = None, ): - self.admin_names = admin_names - self.creator = creator - self.description = description - self.display_name = display_name - self.env_types = env_types - self.extra_infos = extra_infos - self.gmt_create_time = gmt_create_time - self.gmt_modified_time = gmt_modified_time - self.is_default = is_default - self.owner = owner + self.permissions = permissions self.request_id = request_id - self.status = status - self.workspace_id = workspace_id - self.workspace_name = workspace_name def validate(self): - if self.owner: - self.owner.validate() + if self.permissions: + for k in self.permissions: + if k: + k.validate() def to_map(self): _map = super().to_map() @@ -6710,85 +10745,38 @@ def to_map(self): return _map result = dict() - if self.admin_names is not None: - result['AdminNames'] = self.admin_names - if self.creator is not None: - result['Creator'] = self.creator - if self.description is not None: - result['Description'] = self.description - if self.display_name is not None: - result['DisplayName'] = self.display_name - if self.env_types is not None: - result['EnvTypes'] = self.env_types - if self.extra_infos is not None: - result['ExtraInfos'] = self.extra_infos - if self.gmt_create_time is not None: - result['GmtCreateTime'] = self.gmt_create_time - if self.gmt_modified_time is not None: - result['GmtModifiedTime'] = self.gmt_modified_time - if self.is_default is not None: - result['IsDefault'] = self.is_default - if self.owner is not None: - result['Owner'] = self.owner.to_map() + result['Permissions'] = [] + if self.permissions is not None: + for k in self.permissions: + result['Permissions'].append(k.to_map() if k else None) if self.request_id is not None: result['RequestId'] = self.request_id - if self.status is not None: - result['Status'] = self.status - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id - if self.workspace_name is not None: - result['WorkspaceName'] = self.workspace_name return result def from_map(self, m: dict = None): m = m or dict() - if m.get('AdminNames') is not None: - self.admin_names = m.get('AdminNames') - if m.get('Creator') is not None: - self.creator = m.get('Creator') - if m.get('Description') is not None: - self.description = m.get('Description') - if m.get('DisplayName') is not None: - self.display_name = m.get('DisplayName') - if m.get('EnvTypes') is not None: - self.env_types = m.get('EnvTypes') - if m.get('ExtraInfos') is not None: - self.extra_infos = m.get('ExtraInfos') - if m.get('GmtCreateTime') is not None: - self.gmt_create_time = m.get('GmtCreateTime') - if m.get('GmtModifiedTime') is not None: - self.gmt_modified_time = m.get('GmtModifiedTime') - if m.get('IsDefault') is not None: - self.is_default = m.get('IsDefault') - if m.get('Owner') is not None: - temp_model = GetWorkspaceResponseBodyOwner() - self.owner = temp_model.from_map(m['Owner']) + self.permissions = [] + if m.get('Permissions') is not None: + for k in m.get('Permissions'): + temp_model = ListGlobalPermissionsResponseBodyPermissions() + self.permissions.append(temp_model.from_map(k)) if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('Status') is not None: - self.status = m.get('Status') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') - if m.get('WorkspaceName') is not None: - self.workspace_name = m.get('WorkspaceName') return self -class GetWorkspaceResponse(TeaModel): +class ListGlobalPermissionsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetWorkspaceResponseBody = None, + body: ListGlobalPermissionsResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -6813,27 +10801,17 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetWorkspaceResponseBody() + temp_model = ListGlobalPermissionsResponseBody() self.body = temp_model.from_map(m['body']) return self -class ListCodeSourcesRequest(TeaModel): +class ListImageLabelKeysRequest(TeaModel): def __init__( self, - display_name: str = None, - order: str = None, - page_number: int = None, - page_size: int = None, - sort_by: str = None, - workspace_id: str = None, + label_key_prefixes: str = None, ): - self.display_name = display_name - self.order = order - self.page_number = page_number - self.page_size = page_size - self.sort_by = sort_by - self.workspace_id = workspace_id + self.label_key_prefixes = label_key_prefixes def validate(self): pass @@ -6844,53 +10822,30 @@ def to_map(self): return _map result = dict() - if self.display_name is not None: - result['DisplayName'] = self.display_name - if self.order is not None: - result['Order'] = self.order - if self.page_number is not None: - result['PageNumber'] = self.page_number - if self.page_size is not None: - result['PageSize'] = self.page_size - if self.sort_by is not None: - result['SortBy'] = self.sort_by - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id + if self.label_key_prefixes is not None: + result['LabelKeyPrefixes'] = self.label_key_prefixes return result def from_map(self, m: dict = None): m = m or dict() - if m.get('DisplayName') is not None: - self.display_name = m.get('DisplayName') - if m.get('Order') is not None: - self.order = m.get('Order') - if m.get('PageNumber') is not None: - self.page_number = m.get('PageNumber') - if m.get('PageSize') is not None: - self.page_size = m.get('PageSize') - if m.get('SortBy') is not None: - self.sort_by = m.get('SortBy') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') + if m.get('LabelKeyPrefixes') is not None: + self.label_key_prefixes = m.get('LabelKeyPrefixes') return self -class ListCodeSourcesResponseBody(TeaModel): +class ListImageLabelKeysResponseBody(TeaModel): def __init__( self, - code_sources: List[CodeSourceItem] = None, + label_keys: List[Dict[str, List[str]]] = None, request_id: str = None, total_count: int = None, ): - self.code_sources = code_sources + self.label_keys = label_keys self.request_id = request_id self.total_count = total_count def validate(self): - if self.code_sources: - for k in self.code_sources: - if k: - k.validate() + pass def to_map(self): _map = super().to_map() @@ -6898,10 +10853,8 @@ def to_map(self): return _map result = dict() - result['CodeSources'] = [] - if self.code_sources is not None: - for k in self.code_sources: - result['CodeSources'].append(k.to_map() if k else None) + if self.label_keys is not None: + result['LabelKeys'] = self.label_keys if self.request_id is not None: result['RequestId'] = self.request_id if self.total_count is not None: @@ -6910,11 +10863,8 @@ def to_map(self): def from_map(self, m: dict = None): m = m or dict() - self.code_sources = [] - if m.get('CodeSources') is not None: - for k in m.get('CodeSources'): - temp_model = CodeSourceItem() - self.code_sources.append(temp_model.from_map(k)) + if m.get('LabelKeys') is not None: + self.label_keys = m.get('LabelKeys') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') if m.get('TotalCount') is not None: @@ -6922,21 +10872,18 @@ def from_map(self, m: dict = None): return self -class ListCodeSourcesResponse(TeaModel): +class ListImageLabelKeysResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListCodeSourcesResponseBody = None, + body: ListImageLabelKeysResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -6961,17 +10908,25 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListCodeSourcesResponseBody() + temp_model = ListImageLabelKeysResponseBody() self.body = temp_model.from_map(m['body']) return self -class ListConfigsRequest(TeaModel): +class ListImageLabelsRequest(TeaModel): def __init__( self, - config_keys: str = None, + image_id: str = None, + label_filter: str = None, + label_keys: str = None, + region: str = None, + workspace_id: str = None, ): - self.config_keys = config_keys + self.image_id = image_id + self.label_filter = label_filter + self.label_keys = label_keys + self.region = region + self.workspace_id = workspace_id def validate(self): pass @@ -6982,25 +10937,41 @@ def to_map(self): return _map result = dict() - if self.config_keys is not None: - result['ConfigKeys'] = self.config_keys + if self.image_id is not None: + result['ImageId'] = self.image_id + if self.label_filter is not None: + result['LabelFilter'] = self.label_filter + if self.label_keys is not None: + result['LabelKeys'] = self.label_keys + if self.region is not None: + result['Region'] = self.region + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ConfigKeys') is not None: - self.config_keys = m.get('ConfigKeys') + if m.get('ImageId') is not None: + self.image_id = m.get('ImageId') + if m.get('LabelFilter') is not None: + self.label_filter = m.get('LabelFilter') + if m.get('LabelKeys') is not None: + self.label_keys = m.get('LabelKeys') + if m.get('Region') is not None: + self.region = m.get('Region') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class ListConfigsResponseBodyConfigs(TeaModel): +class ListImageLabelsResponseBodyLabels(TeaModel): def __init__( self, - config_key: str = None, - config_value: str = None, + key: str = None, + value: str = None, ): - self.config_key = config_key - self.config_value = config_value + self.key = key + self.value = value def validate(self): pass @@ -7011,35 +10982,35 @@ def to_map(self): return _map result = dict() - if self.config_key is not None: - result['ConfigKey'] = self.config_key - if self.config_value is not None: - result['ConfigValue'] = self.config_value + if self.key is not None: + result['Key'] = self.key + if self.value is not None: + result['Value'] = self.value return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ConfigKey') is not None: - self.config_key = m.get('ConfigKey') - if m.get('ConfigValue') is not None: - self.config_value = m.get('ConfigValue') + if m.get('Key') is not None: + self.key = m.get('Key') + if m.get('Value') is not None: + self.value = m.get('Value') return self -class ListConfigsResponseBody(TeaModel): +class ListImageLabelsResponseBody(TeaModel): def __init__( self, - configs: List[ListConfigsResponseBodyConfigs] = None, + labels: List[ListImageLabelsResponseBodyLabels] = None, request_id: str = None, total_count: int = None, ): - self.configs = configs + self.labels = labels self.request_id = request_id self.total_count = total_count def validate(self): - if self.configs: - for k in self.configs: + if self.labels: + for k in self.labels: if k: k.validate() @@ -7049,10 +11020,10 @@ def to_map(self): return _map result = dict() - result['Configs'] = [] - if self.configs is not None: - for k in self.configs: - result['Configs'].append(k.to_map() if k else None) + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) if self.request_id is not None: result['RequestId'] = self.request_id if self.total_count is not None: @@ -7061,11 +11032,11 @@ def to_map(self): def from_map(self, m: dict = None): m = m or dict() - self.configs = [] - if m.get('Configs') is not None: - for k in m.get('Configs'): - temp_model = ListConfigsResponseBodyConfigs() - self.configs.append(temp_model.from_map(k)) + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = ListImageLabelsResponseBodyLabels() + self.labels.append(temp_model.from_map(k)) if m.get('RequestId') is not None: self.request_id = m.get('RequestId') if m.get('TotalCount') is not None: @@ -7073,21 +11044,18 @@ def from_map(self, m: dict = None): return self -class ListConfigsResponse(TeaModel): +class ListImageLabelsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListConfigsResponseBody = None, + body: ListImageLabelsResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -7112,40 +11080,38 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListConfigsResponseBody() + temp_model = ListImageLabelsResponseBody() self.body = temp_model.from_map(m['body']) return self -class ListDatasetsRequest(TeaModel): +class ListImagesRequest(TeaModel): def __init__( self, - data_source_types: str = None, - data_types: str = None, - label: str = None, - label_keys: str = None, - label_values: str = None, + accessibility: str = None, + labels: str = None, name: str = None, order: str = None, page_number: int = None, page_size: int = None, - properties: str = None, - source_id: str = None, - source_types: str = None, + parent_user_id: str = None, + query: str = None, + sort_by: str = None, + user_id: str = None, + verbose: bool = None, workspace_id: str = None, ): - self.data_source_types = data_source_types - self.data_types = data_types - self.label = label - self.label_keys = label_keys - self.label_values = label_values + self.accessibility = accessibility + self.labels = labels self.name = name self.order = order self.page_number = page_number self.page_size = page_size - self.properties = properties - self.source_id = source_id - self.source_types = source_types + self.parent_user_id = parent_user_id + self.query = query + self.sort_by = sort_by + self.user_id = user_id + self.verbose = verbose self.workspace_id = workspace_id def validate(self): @@ -7157,16 +11123,10 @@ def to_map(self): return _map result = dict() - if self.data_source_types is not None: - result['DataSourceTypes'] = self.data_source_types - if self.data_types is not None: - result['DataTypes'] = self.data_types - if self.label is not None: - result['Label'] = self.label - if self.label_keys is not None: - result['LabelKeys'] = self.label_keys - if self.label_values is not None: - result['LabelValues'] = self.label_values + if self.accessibility is not None: + result['Accessibility'] = self.accessibility + if self.labels is not None: + result['Labels'] = self.labels if self.name is not None: result['Name'] = self.name if self.order is not None: @@ -7175,28 +11135,26 @@ def to_map(self): result['PageNumber'] = self.page_number if self.page_size is not None: result['PageSize'] = self.page_size - if self.properties is not None: - result['Properties'] = self.properties - if self.source_id is not None: - result['SourceId'] = self.source_id - if self.source_types is not None: - result['SourceTypes'] = self.source_types + if self.parent_user_id is not None: + result['ParentUserId'] = self.parent_user_id + if self.query is not None: + result['Query'] = self.query + if self.sort_by is not None: + result['SortBy'] = self.sort_by + if self.user_id is not None: + result['UserId'] = self.user_id + if self.verbose is not None: + result['Verbose'] = self.verbose if self.workspace_id is not None: result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('DataSourceTypes') is not None: - self.data_source_types = m.get('DataSourceTypes') - if m.get('DataTypes') is not None: - self.data_types = m.get('DataTypes') - if m.get('Label') is not None: - self.label = m.get('Label') - if m.get('LabelKeys') is not None: - self.label_keys = m.get('LabelKeys') - if m.get('LabelValues') is not None: - self.label_values = m.get('LabelValues') + if m.get('Accessibility') is not None: + self.accessibility = m.get('Accessibility') + if m.get('Labels') is not None: + self.labels = m.get('Labels') if m.get('Name') is not None: self.name = m.get('Name') if m.get('Order') is not None: @@ -7205,81 +11163,32 @@ def from_map(self, m: dict = None): self.page_number = m.get('PageNumber') if m.get('PageSize') is not None: self.page_size = m.get('PageSize') - if m.get('Properties') is not None: - self.properties = m.get('Properties') - if m.get('SourceId') is not None: - self.source_id = m.get('SourceId') - if m.get('SourceTypes') is not None: - self.source_types = m.get('SourceTypes') + if m.get('ParentUserId') is not None: + self.parent_user_id = m.get('ParentUserId') + if m.get('Query') is not None: + self.query = m.get('Query') + if m.get('SortBy') is not None: + self.sort_by = m.get('SortBy') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + if m.get('Verbose') is not None: + self.verbose = m.get('Verbose') if m.get('WorkspaceId') is not None: self.workspace_id = m.get('WorkspaceId') return self -class ListDatasetsResponseBody(TeaModel): - def __init__( - self, - datasets: List[Dataset] = None, - request_id: str = None, - total_count: int = None, - ): - self.datasets = datasets - self.request_id = request_id - self.total_count = total_count - - def validate(self): - if self.datasets: - for k in self.datasets: - if k: - k.validate() - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - result['Datasets'] = [] - if self.datasets is not None: - for k in self.datasets: - result['Datasets'].append(k.to_map() if k else None) - if self.request_id is not None: - result['RequestId'] = self.request_id - if self.total_count is not None: - result['TotalCount'] = self.total_count - return result - - def from_map(self, m: dict = None): - m = m or dict() - self.datasets = [] - if m.get('Datasets') is not None: - for k in m.get('Datasets'): - temp_model = Dataset() - self.datasets.append(temp_model.from_map(k)) - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - if m.get('TotalCount') is not None: - self.total_count = m.get('TotalCount') - return self - - -class ListDatasetsResponse(TeaModel): +class ListImagesResponseBodyImagesLabels(TeaModel): def __init__( self, - headers: Dict[str, str] = None, - status_code: int = None, - body: ListDatasetsResponseBody = None, + key: str = None, + value: str = None, ): - self.headers = headers - self.status_code = status_code - self.body = body + self.key = key + self.value = value def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') - if self.body: - self.body.validate() + pass def to_map(self): _map = super().to_map() @@ -7287,35 +11196,55 @@ def to_map(self): return _map result = dict() - if self.headers is not None: - result['headers'] = self.headers - if self.status_code is not None: - result['statusCode'] = self.status_code - if self.body is not None: - result['body'] = self.body.to_map() + if self.key is not None: + result['Key'] = self.key + if self.value is not None: + result['Value'] = self.value return result def from_map(self, m: dict = None): m = m or dict() - if m.get('headers') is not None: - self.headers = m.get('headers') - if m.get('statusCode') is not None: - self.status_code = m.get('statusCode') - if m.get('body') is not None: - temp_model = ListDatasetsResponseBody() - self.body = temp_model.from_map(m['body']) + if m.get('Key') is not None: + self.key = m.get('Key') + if m.get('Value') is not None: + self.value = m.get('Value') return self -class ListFeaturesRequest(TeaModel): +class ListImagesResponseBodyImages(TeaModel): def __init__( self, - names: str = None, + accessibility: str = None, + description: str = None, + gmt_create_time: str = None, + gmt_modified_time: str = None, + image_id: str = None, + image_uri: str = None, + labels: List[ListImagesResponseBodyImagesLabels] = None, + name: str = None, + parent_user_id: str = None, + size: int = None, + user_id: str = None, + workspace_id: str = None, ): - self.names = names + self.accessibility = accessibility + self.description = description + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time + self.image_id = image_id + self.image_uri = image_uri + self.labels = labels + self.name = name + self.parent_user_id = parent_user_id + self.size = size + self.user_id = user_id + self.workspace_id = workspace_id def validate(self): - pass + if self.labels: + for k in self.labels: + if k: + k.validate() def to_map(self): _map = super().to_map() @@ -7323,30 +11252,82 @@ def to_map(self): return _map result = dict() - if self.names is not None: - result['Names'] = self.names + if self.accessibility is not None: + result['Accessibility'] = self.accessibility + if self.description is not None: + result['Description'] = self.description + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + if self.image_id is not None: + result['ImageId'] = self.image_id + if self.image_uri is not None: + result['ImageUri'] = self.image_uri + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + if self.name is not None: + result['Name'] = self.name + if self.parent_user_id is not None: + result['ParentUserId'] = self.parent_user_id + if self.size is not None: + result['Size'] = self.size + if self.user_id is not None: + result['UserId'] = self.user_id + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Names') is not None: - self.names = m.get('Names') + if m.get('Accessibility') is not None: + self.accessibility = m.get('Accessibility') + if m.get('Description') is not None: + self.description = m.get('Description') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + if m.get('ImageId') is not None: + self.image_id = m.get('ImageId') + if m.get('ImageUri') is not None: + self.image_uri = m.get('ImageUri') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = ListImagesResponseBodyImagesLabels() + self.labels.append(temp_model.from_map(k)) + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('ParentUserId') is not None: + self.parent_user_id = m.get('ParentUserId') + if m.get('Size') is not None: + self.size = m.get('Size') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class ListFeaturesResponseBody(TeaModel): +class ListImagesResponseBody(TeaModel): def __init__( self, - features: List[str] = None, + images: List[ListImagesResponseBodyImages] = None, request_id: str = None, total_count: int = None, ): - self.features = features + self.images = images self.request_id = request_id self.total_count = total_count def validate(self): - pass + if self.images: + for k in self.images: + if k: + k.validate() def to_map(self): _map = super().to_map() @@ -7354,8 +11335,10 @@ def to_map(self): return _map result = dict() - if self.features is not None: - result['Features'] = self.features + result['Images'] = [] + if self.images is not None: + for k in self.images: + result['Images'].append(k.to_map() if k else None) if self.request_id is not None: result['RequestId'] = self.request_id if self.total_count is not None: @@ -7364,8 +11347,11 @@ def to_map(self): def from_map(self, m: dict = None): m = m or dict() - if m.get('Features') is not None: - self.features = m.get('Features') + self.images = [] + if m.get('Images') is not None: + for k in m.get('Images'): + temp_model = ListImagesResponseBodyImages() + self.images.append(temp_model.from_map(k)) if m.get('RequestId') is not None: self.request_id = m.get('RequestId') if m.get('TotalCount') is not None: @@ -7373,21 +11359,18 @@ def from_map(self, m: dict = None): return self -class ListFeaturesResponse(TeaModel): +class ListImagesResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListFeaturesResponseBody = None, + body: ListImagesResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -7412,19 +11395,23 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListFeaturesResponseBody() + temp_model = ListImagesResponseBody() self.body = temp_model.from_map(m['body']) return self -class ListGlobalPermissionsResponseBodyPermissionsPermissionRules(TeaModel): +class ListMembersRequest(TeaModel): def __init__( self, - accessibility: str = None, - entity_access_type: str = None, + member_name: str = None, + page_number: int = None, + page_size: int = None, + roles: str = None, ): - self.accessibility = accessibility - self.entity_access_type = entity_access_type + self.member_name = member_name + self.page_number = page_number + self.page_size = page_size + self.roles = roles def validate(self): pass @@ -7435,35 +11422,48 @@ def to_map(self): return _map result = dict() - if self.accessibility is not None: - result['Accessibility'] = self.accessibility - if self.entity_access_type is not None: - result['EntityAccessType'] = self.entity_access_type + if self.member_name is not None: + result['MemberName'] = self.member_name + if self.page_number is not None: + result['PageNumber'] = self.page_number + if self.page_size is not None: + result['PageSize'] = self.page_size + if self.roles is not None: + result['Roles'] = self.roles return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Accessibility') is not None: - self.accessibility = m.get('Accessibility') - if m.get('EntityAccessType') is not None: - self.entity_access_type = m.get('EntityAccessType') + if m.get('MemberName') is not None: + self.member_name = m.get('MemberName') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + if m.get('Roles') is not None: + self.roles = m.get('Roles') return self -class ListGlobalPermissionsResponseBodyPermissions(TeaModel): +class ListMembersResponseBodyMembers(TeaModel): def __init__( self, - permission_code: str = None, - permission_rules: List[ListGlobalPermissionsResponseBodyPermissionsPermissionRules] = None, + display_name: str = None, + gmt_create_time: str = None, + member_id: str = None, + member_name: str = None, + roles: List[str] = None, + user_id: str = None, ): - self.permission_code = permission_code - self.permission_rules = permission_rules + self.display_name = display_name + self.gmt_create_time = gmt_create_time + self.member_id = member_id + self.member_name = member_name + self.roles = roles + self.user_id = user_id def validate(self): - if self.permission_rules: - for k in self.permission_rules: - if k: - k.validate() + pass def to_map(self): _map = super().to_map() @@ -7471,38 +11471,51 @@ def to_map(self): return _map result = dict() - if self.permission_code is not None: - result['PermissionCode'] = self.permission_code - result['PermissionRules'] = [] - if self.permission_rules is not None: - for k in self.permission_rules: - result['PermissionRules'].append(k.to_map() if k else None) + if self.display_name is not None: + result['DisplayName'] = self.display_name + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.member_id is not None: + result['MemberId'] = self.member_id + if self.member_name is not None: + result['MemberName'] = self.member_name + if self.roles is not None: + result['Roles'] = self.roles + if self.user_id is not None: + result['UserId'] = self.user_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('PermissionCode') is not None: - self.permission_code = m.get('PermissionCode') - self.permission_rules = [] - if m.get('PermissionRules') is not None: - for k in m.get('PermissionRules'): - temp_model = ListGlobalPermissionsResponseBodyPermissionsPermissionRules() - self.permission_rules.append(temp_model.from_map(k)) + if m.get('DisplayName') is not None: + self.display_name = m.get('DisplayName') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('MemberId') is not None: + self.member_id = m.get('MemberId') + if m.get('MemberName') is not None: + self.member_name = m.get('MemberName') + if m.get('Roles') is not None: + self.roles = m.get('Roles') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') return self -class ListGlobalPermissionsResponseBody(TeaModel): +class ListMembersResponseBody(TeaModel): def __init__( self, - permissions: List[ListGlobalPermissionsResponseBodyPermissions] = None, + members: List[ListMembersResponseBodyMembers] = None, request_id: str = None, + total_count: int = None, ): - self.permissions = permissions + self.members = members self.request_id = request_id + self.total_count = total_count def validate(self): - if self.permissions: - for k in self.permissions: + if self.members: + for k in self.members: if k: k.validate() @@ -7512,41 +11525,42 @@ def to_map(self): return _map result = dict() - result['Permissions'] = [] - if self.permissions is not None: - for k in self.permissions: - result['Permissions'].append(k.to_map() if k else None) + result['Members'] = [] + if self.members is not None: + for k in self.members: + result['Members'].append(k.to_map() if k else None) if self.request_id is not None: result['RequestId'] = self.request_id + if self.total_count is not None: + result['TotalCount'] = self.total_count return result def from_map(self, m: dict = None): m = m or dict() - self.permissions = [] - if m.get('Permissions') is not None: - for k in m.get('Permissions'): - temp_model = ListGlobalPermissionsResponseBodyPermissions() - self.permissions.append(temp_model.from_map(k)) + self.members = [] + if m.get('Members') is not None: + for k in m.get('Members'): + temp_model = ListMembersResponseBodyMembers() + self.members.append(temp_model.from_map(k)) if m.get('RequestId') is not None: self.request_id = m.get('RequestId') + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') return self -class ListGlobalPermissionsResponse(TeaModel): +class ListMembersResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListGlobalPermissionsResponseBody = None, + body: ListMembersResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -7571,25 +11585,17 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListGlobalPermissionsResponseBody() + temp_model = ListMembersResponseBody() self.body = temp_model.from_map(m['body']) return self -class ListImageLabelsRequest(TeaModel): +class ListModelDomainsRequest(TeaModel): def __init__( self, - image_id: str = None, - label_filter: str = None, - label_keys: str = None, - region: str = None, - workspace_id: str = None, + model_domain_ids: str = None, ): - self.image_id = image_id - self.label_filter = label_filter - self.label_keys = label_keys - self.region = region - self.workspace_id = workspace_id + self.model_domain_ids = model_domain_ids def validate(self): pass @@ -7600,44 +11606,86 @@ def to_map(self): return _map result = dict() - if self.image_id is not None: - result['ImageId'] = self.image_id - if self.label_filter is not None: - result['LabelFilter'] = self.label_filter - if self.label_keys is not None: - result['LabelKeys'] = self.label_keys - if self.region is not None: - result['Region'] = self.region - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id + if self.model_domain_ids is not None: + result['ModelDomainIds'] = self.model_domain_ids + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('ModelDomainIds') is not None: + self.model_domain_ids = m.get('ModelDomainIds') + return self + + +class ListModelDomainsResponseBodyModelDomainsModelTasks(TeaModel): + def __init__( + self, + model_domain_id: str = None, + model_task_id: str = None, + model_task_name: str = None, + order_number: int = None, + search_words: str = None, + ): + self.model_domain_id = model_domain_id + self.model_task_id = model_task_id + self.model_task_name = model_task_name + self.order_number = order_number + self.search_words = search_words + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.model_domain_id is not None: + result['ModelDomainId'] = self.model_domain_id + if self.model_task_id is not None: + result['ModelTaskId'] = self.model_task_id + if self.model_task_name is not None: + result['ModelTaskName'] = self.model_task_name + if self.order_number is not None: + result['OrderNumber'] = self.order_number + if self.search_words is not None: + result['SearchWords'] = self.search_words return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ImageId') is not None: - self.image_id = m.get('ImageId') - if m.get('LabelFilter') is not None: - self.label_filter = m.get('LabelFilter') - if m.get('LabelKeys') is not None: - self.label_keys = m.get('LabelKeys') - if m.get('Region') is not None: - self.region = m.get('Region') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') + if m.get('ModelDomainId') is not None: + self.model_domain_id = m.get('ModelDomainId') + if m.get('ModelTaskId') is not None: + self.model_task_id = m.get('ModelTaskId') + if m.get('ModelTaskName') is not None: + self.model_task_name = m.get('ModelTaskName') + if m.get('OrderNumber') is not None: + self.order_number = m.get('OrderNumber') + if m.get('SearchWords') is not None: + self.search_words = m.get('SearchWords') return self -class ListImageLabelsResponseBodyLabels(TeaModel): +class ListModelDomainsResponseBodyModelDomains(TeaModel): def __init__( self, - key: str = None, - value: str = None, + model_domain_id: str = None, + model_domain_name: str = None, + model_tasks: List[ListModelDomainsResponseBodyModelDomainsModelTasks] = None, + order_number: int = None, ): - self.key = key - self.value = value + self.model_domain_id = model_domain_id + self.model_domain_name = model_domain_name + self.model_tasks = model_tasks + self.order_number = order_number def validate(self): - pass + if self.model_tasks: + for k in self.model_tasks: + if k: + k.validate() def to_map(self): _map = super().to_map() @@ -7645,35 +11693,48 @@ def to_map(self): return _map result = dict() - if self.key is not None: - result['Key'] = self.key - if self.value is not None: - result['Value'] = self.value + if self.model_domain_id is not None: + result['ModelDomainId'] = self.model_domain_id + if self.model_domain_name is not None: + result['ModelDomainName'] = self.model_domain_name + result['ModelTasks'] = [] + if self.model_tasks is not None: + for k in self.model_tasks: + result['ModelTasks'].append(k.to_map() if k else None) + if self.order_number is not None: + result['OrderNumber'] = self.order_number return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Key') is not None: - self.key = m.get('Key') - if m.get('Value') is not None: - self.value = m.get('Value') + if m.get('ModelDomainId') is not None: + self.model_domain_id = m.get('ModelDomainId') + if m.get('ModelDomainName') is not None: + self.model_domain_name = m.get('ModelDomainName') + self.model_tasks = [] + if m.get('ModelTasks') is not None: + for k in m.get('ModelTasks'): + temp_model = ListModelDomainsResponseBodyModelDomainsModelTasks() + self.model_tasks.append(temp_model.from_map(k)) + if m.get('OrderNumber') is not None: + self.order_number = m.get('OrderNumber') return self -class ListImageLabelsResponseBody(TeaModel): +class ListModelDomainsResponseBody(TeaModel): def __init__( self, - labels: List[ListImageLabelsResponseBodyLabels] = None, + model_domains: List[ListModelDomainsResponseBodyModelDomains] = None, request_id: str = None, total_count: int = None, ): - self.labels = labels + self.model_domains = model_domains self.request_id = request_id self.total_count = total_count def validate(self): - if self.labels: - for k in self.labels: + if self.model_domains: + for k in self.model_domains: if k: k.validate() @@ -7683,10 +11744,10 @@ def to_map(self): return _map result = dict() - result['Labels'] = [] - if self.labels is not None: - for k in self.labels: - result['Labels'].append(k.to_map() if k else None) + result['ModelDomains'] = [] + if self.model_domains is not None: + for k in self.model_domains: + result['ModelDomains'].append(k.to_map() if k else None) if self.request_id is not None: result['RequestId'] = self.request_id if self.total_count is not None: @@ -7695,11 +11756,11 @@ def to_map(self): def from_map(self, m: dict = None): m = m or dict() - self.labels = [] - if m.get('Labels') is not None: - for k in m.get('Labels'): - temp_model = ListImageLabelsResponseBodyLabels() - self.labels.append(temp_model.from_map(k)) + self.model_domains = [] + if m.get('ModelDomains') is not None: + for k in m.get('ModelDomains'): + temp_model = ListModelDomainsResponseBodyModelDomains() + self.model_domains.append(temp_model.from_map(k)) if m.get('RequestId') is not None: self.request_id = m.get('RequestId') if m.get('TotalCount') is not None: @@ -7707,21 +11768,18 @@ def from_map(self, m: dict = None): return self -class ListImageLabelsResponse(TeaModel): +class ListModelDomainsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListImageLabelsResponseBody = None, + body: ListModelDomainsResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -7746,163 +11804,44 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListImageLabelsResponseBody() + temp_model = ListModelDomainsResponseBody() self.body = temp_model.from_map(m['body']) return self -class ListImagesRequest(TeaModel): +class ListModelVersionsRequest(TeaModel): def __init__( self, + approval_status: str = None, + format_type: str = None, + framework_type: str = None, + label: str = None, + label_string: str = None, labels: str = None, - name: str = None, order: str = None, page_number: int = None, page_size: int = None, - parent_user_id: str = None, - query: str = None, sort_by: str = None, - user_id: str = None, - verbose: bool = None, - workspace_id: str = None, + source_id: str = None, + source_type: str = None, + version_name: str = None, ): + self.approval_status = approval_status + self.format_type = format_type + self.framework_type = framework_type + self.label = label + self.label_string = label_string self.labels = labels - self.name = name self.order = order self.page_number = page_number self.page_size = page_size - self.parent_user_id = parent_user_id - self.query = query self.sort_by = sort_by - self.user_id = user_id - self.verbose = verbose - self.workspace_id = workspace_id - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.labels is not None: - result['Labels'] = self.labels - if self.name is not None: - result['Name'] = self.name - if self.order is not None: - result['Order'] = self.order - if self.page_number is not None: - result['PageNumber'] = self.page_number - if self.page_size is not None: - result['PageSize'] = self.page_size - if self.parent_user_id is not None: - result['ParentUserId'] = self.parent_user_id - if self.query is not None: - result['Query'] = self.query - if self.sort_by is not None: - result['SortBy'] = self.sort_by - if self.user_id is not None: - result['UserId'] = self.user_id - if self.verbose is not None: - result['Verbose'] = self.verbose - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('Labels') is not None: - self.labels = m.get('Labels') - if m.get('Name') is not None: - self.name = m.get('Name') - if m.get('Order') is not None: - self.order = m.get('Order') - if m.get('PageNumber') is not None: - self.page_number = m.get('PageNumber') - if m.get('PageSize') is not None: - self.page_size = m.get('PageSize') - if m.get('ParentUserId') is not None: - self.parent_user_id = m.get('ParentUserId') - if m.get('Query') is not None: - self.query = m.get('Query') - if m.get('SortBy') is not None: - self.sort_by = m.get('SortBy') - if m.get('UserId') is not None: - self.user_id = m.get('UserId') - if m.get('Verbose') is not None: - self.verbose = m.get('Verbose') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') - return self - - -class ListImagesResponseBodyImagesLabels(TeaModel): - def __init__( - self, - key: str = None, - value: str = None, - ): - self.key = key - self.value = value - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.key is not None: - result['Key'] = self.key - if self.value is not None: - result['Value'] = self.value - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('Key') is not None: - self.key = m.get('Key') - if m.get('Value') is not None: - self.value = m.get('Value') - return self - - -class ListImagesResponseBodyImages(TeaModel): - def __init__( - self, - accessibility: str = None, - description: str = None, - gmt_create_time: str = None, - gmt_modified_time: str = None, - image_id: str = None, - image_uri: str = None, - labels: List[ListImagesResponseBodyImagesLabels] = None, - name: str = None, - parent_user_id: str = None, - user_id: str = None, - workspace_id: str = None, - ): - self.accessibility = accessibility - self.description = description - self.gmt_create_time = gmt_create_time - self.gmt_modified_time = gmt_modified_time - self.image_id = image_id - self.image_uri = image_uri - self.labels = labels - self.name = name - self.parent_user_id = parent_user_id - self.user_id = user_id - self.workspace_id = workspace_id + self.source_id = source_id + self.source_type = source_type + self.version_name = version_name def validate(self): - if self.labels: - for k in self.labels: - if k: - k.validate() + pass def to_map(self): _map = super().to_map() @@ -7910,76 +11849,79 @@ def to_map(self): return _map result = dict() - if self.accessibility is not None: - result['Accessibility'] = self.accessibility - if self.description is not None: - result['Description'] = self.description - if self.gmt_create_time is not None: - result['GmtCreateTime'] = self.gmt_create_time - if self.gmt_modified_time is not None: - result['GmtModifiedTime'] = self.gmt_modified_time - if self.image_id is not None: - result['ImageId'] = self.image_id - if self.image_uri is not None: - result['ImageUri'] = self.image_uri - result['Labels'] = [] + if self.approval_status is not None: + result['ApprovalStatus'] = self.approval_status + if self.format_type is not None: + result['FormatType'] = self.format_type + if self.framework_type is not None: + result['FrameworkType'] = self.framework_type + if self.label is not None: + result['Label'] = self.label + if self.label_string is not None: + result['LabelString'] = self.label_string if self.labels is not None: - for k in self.labels: - result['Labels'].append(k.to_map() if k else None) - if self.name is not None: - result['Name'] = self.name - if self.parent_user_id is not None: - result['ParentUserId'] = self.parent_user_id - if self.user_id is not None: - result['UserId'] = self.user_id - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id + result['Labels'] = self.labels + if self.order is not None: + result['Order'] = self.order + if self.page_number is not None: + result['PageNumber'] = self.page_number + if self.page_size is not None: + result['PageSize'] = self.page_size + if self.sort_by is not None: + result['SortBy'] = self.sort_by + if self.source_id is not None: + result['SourceId'] = self.source_id + if self.source_type is not None: + result['SourceType'] = self.source_type + if self.version_name is not None: + result['VersionName'] = self.version_name return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Accessibility') is not None: - self.accessibility = m.get('Accessibility') - if m.get('Description') is not None: - self.description = m.get('Description') - if m.get('GmtCreateTime') is not None: - self.gmt_create_time = m.get('GmtCreateTime') - if m.get('GmtModifiedTime') is not None: - self.gmt_modified_time = m.get('GmtModifiedTime') - if m.get('ImageId') is not None: - self.image_id = m.get('ImageId') - if m.get('ImageUri') is not None: - self.image_uri = m.get('ImageUri') - self.labels = [] + if m.get('ApprovalStatus') is not None: + self.approval_status = m.get('ApprovalStatus') + if m.get('FormatType') is not None: + self.format_type = m.get('FormatType') + if m.get('FrameworkType') is not None: + self.framework_type = m.get('FrameworkType') + if m.get('Label') is not None: + self.label = m.get('Label') + if m.get('LabelString') is not None: + self.label_string = m.get('LabelString') if m.get('Labels') is not None: - for k in m.get('Labels'): - temp_model = ListImagesResponseBodyImagesLabels() - self.labels.append(temp_model.from_map(k)) - if m.get('Name') is not None: - self.name = m.get('Name') - if m.get('ParentUserId') is not None: - self.parent_user_id = m.get('ParentUserId') - if m.get('UserId') is not None: - self.user_id = m.get('UserId') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') + self.labels = m.get('Labels') + if m.get('Order') is not None: + self.order = m.get('Order') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + if m.get('SortBy') is not None: + self.sort_by = m.get('SortBy') + if m.get('SourceId') is not None: + self.source_id = m.get('SourceId') + if m.get('SourceType') is not None: + self.source_type = m.get('SourceType') + if m.get('VersionName') is not None: + self.version_name = m.get('VersionName') return self -class ListImagesResponseBody(TeaModel): +class ListModelVersionsResponseBody(TeaModel): def __init__( self, - images: List[ListImagesResponseBodyImages] = None, request_id: str = None, total_count: int = None, + versions: List[ModelVersion] = None, ): - self.images = images self.request_id = request_id self.total_count = total_count + self.versions = versions def validate(self): - if self.images: - for k in self.images: + if self.versions: + for k in self.versions: if k: k.validate() @@ -7989,45 +11931,42 @@ def to_map(self): return _map result = dict() - result['Images'] = [] - if self.images is not None: - for k in self.images: - result['Images'].append(k.to_map() if k else None) if self.request_id is not None: result['RequestId'] = self.request_id if self.total_count is not None: result['TotalCount'] = self.total_count + result['Versions'] = [] + if self.versions is not None: + for k in self.versions: + result['Versions'].append(k.to_map() if k else None) return result def from_map(self, m: dict = None): m = m or dict() - self.images = [] - if m.get('Images') is not None: - for k in m.get('Images'): - temp_model = ListImagesResponseBodyImages() - self.images.append(temp_model.from_map(k)) if m.get('RequestId') is not None: self.request_id = m.get('RequestId') if m.get('TotalCount') is not None: self.total_count = m.get('TotalCount') + self.versions = [] + if m.get('Versions') is not None: + for k in m.get('Versions'): + temp_model = ModelVersion() + self.versions.append(temp_model.from_map(k)) return self -class ListImagesResponse(TeaModel): +class ListModelVersionsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListImagesResponseBody = None, + body: ListModelVersionsResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -8052,23 +11991,47 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListImagesResponseBody() + temp_model = ListModelVersionsResponseBody() self.body = temp_model.from_map(m['body']) return self -class ListMembersRequest(TeaModel): +class ListModelsRequest(TeaModel): def __init__( self, - member_name: str = None, + collections: str = None, + domain: str = None, + label: str = None, + label_string: str = None, + labels: str = None, + model_name: str = None, + model_type: str = None, + order: str = None, + origin: str = None, page_number: int = None, page_size: int = None, - roles: str = None, + provider: str = None, + query: str = None, + sort_by: str = None, + task: str = None, + workspace_id: str = None, ): - self.member_name = member_name + self.collections = collections + self.domain = domain + self.label = label + self.label_string = label_string + self.labels = labels + self.model_name = model_name + self.model_type = model_type + self.order = order + self.origin = origin self.page_number = page_number self.page_size = page_size - self.roles = roles + self.provider = provider + self.query = query + self.sort_by = sort_by + self.task = task + self.workspace_id = workspace_id def validate(self): pass @@ -8079,100 +12042,91 @@ def to_map(self): return _map result = dict() - if self.member_name is not None: - result['MemberName'] = self.member_name + if self.collections is not None: + result['Collections'] = self.collections + if self.domain is not None: + result['Domain'] = self.domain + if self.label is not None: + result['Label'] = self.label + if self.label_string is not None: + result['LabelString'] = self.label_string + if self.labels is not None: + result['Labels'] = self.labels + if self.model_name is not None: + result['ModelName'] = self.model_name + if self.model_type is not None: + result['ModelType'] = self.model_type + if self.order is not None: + result['Order'] = self.order + if self.origin is not None: + result['Origin'] = self.origin if self.page_number is not None: result['PageNumber'] = self.page_number if self.page_size is not None: result['PageSize'] = self.page_size - if self.roles is not None: - result['Roles'] = self.roles - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('MemberName') is not None: - self.member_name = m.get('MemberName') - if m.get('PageNumber') is not None: - self.page_number = m.get('PageNumber') - if m.get('PageSize') is not None: - self.page_size = m.get('PageSize') - if m.get('Roles') is not None: - self.roles = m.get('Roles') - return self - - -class ListMembersResponseBodyMembers(TeaModel): - def __init__( - self, - display_name: str = None, - gmt_create_time: str = None, - member_id: str = None, - member_name: str = None, - roles: List[str] = None, - user_id: str = None, - ): - self.display_name = display_name - self.gmt_create_time = gmt_create_time - self.member_id = member_id - self.member_name = member_name - self.roles = roles - self.user_id = user_id - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.display_name is not None: - result['DisplayName'] = self.display_name - if self.gmt_create_time is not None: - result['GmtCreateTime'] = self.gmt_create_time - if self.member_id is not None: - result['MemberId'] = self.member_id - if self.member_name is not None: - result['MemberName'] = self.member_name - if self.roles is not None: - result['Roles'] = self.roles - if self.user_id is not None: - result['UserId'] = self.user_id + if self.provider is not None: + result['Provider'] = self.provider + if self.query is not None: + result['Query'] = self.query + if self.sort_by is not None: + result['SortBy'] = self.sort_by + if self.task is not None: + result['Task'] = self.task + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): - m = m or dict() - if m.get('DisplayName') is not None: - self.display_name = m.get('DisplayName') - if m.get('GmtCreateTime') is not None: - self.gmt_create_time = m.get('GmtCreateTime') - if m.get('MemberId') is not None: - self.member_id = m.get('MemberId') - if m.get('MemberName') is not None: - self.member_name = m.get('MemberName') - if m.get('Roles') is not None: - self.roles = m.get('Roles') - if m.get('UserId') is not None: - self.user_id = m.get('UserId') + m = m or dict() + if m.get('Collections') is not None: + self.collections = m.get('Collections') + if m.get('Domain') is not None: + self.domain = m.get('Domain') + if m.get('Label') is not None: + self.label = m.get('Label') + if m.get('LabelString') is not None: + self.label_string = m.get('LabelString') + if m.get('Labels') is not None: + self.labels = m.get('Labels') + if m.get('ModelName') is not None: + self.model_name = m.get('ModelName') + if m.get('ModelType') is not None: + self.model_type = m.get('ModelType') + if m.get('Order') is not None: + self.order = m.get('Order') + if m.get('Origin') is not None: + self.origin = m.get('Origin') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + if m.get('Provider') is not None: + self.provider = m.get('Provider') + if m.get('Query') is not None: + self.query = m.get('Query') + if m.get('SortBy') is not None: + self.sort_by = m.get('SortBy') + if m.get('Task') is not None: + self.task = m.get('Task') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class ListMembersResponseBody(TeaModel): +class ListModelsResponseBody(TeaModel): def __init__( self, - members: List[ListMembersResponseBodyMembers] = None, + models: List[Model] = None, request_id: str = None, total_count: int = None, ): - self.members = members + self.models = models self.request_id = request_id self.total_count = total_count def validate(self): - if self.members: - for k in self.members: + if self.models: + for k in self.models: if k: k.validate() @@ -8182,10 +12136,10 @@ def to_map(self): return _map result = dict() - result['Members'] = [] - if self.members is not None: - for k in self.members: - result['Members'].append(k.to_map() if k else None) + result['Models'] = [] + if self.models is not None: + for k in self.models: + result['Models'].append(k.to_map() if k else None) if self.request_id is not None: result['RequestId'] = self.request_id if self.total_count is not None: @@ -8194,11 +12148,11 @@ def to_map(self): def from_map(self, m: dict = None): m = m or dict() - self.members = [] - if m.get('Members') is not None: - for k in m.get('Members'): - temp_model = ListMembersResponseBodyMembers() - self.members.append(temp_model.from_map(k)) + self.models = [] + if m.get('Models') is not None: + for k in m.get('Models'): + temp_model = Model() + self.models.append(temp_model.from_map(k)) if m.get('RequestId') is not None: self.request_id = m.get('RequestId') if m.get('TotalCount') is not None: @@ -8206,21 +12160,18 @@ def from_map(self, m: dict = None): return self -class ListMembersResponse(TeaModel): +class ListModelsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListMembersResponseBody = None, + body: ListModelsResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -8245,17 +12196,19 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListMembersResponseBody() + temp_model = ListModelsResponseBody() self.body = temp_model.from_map(m['body']) return self -class ListModelDomainsRequest(TeaModel): +class ListModuleConfigsRequest(TeaModel): def __init__( self, - model_domain_ids: str = None, + module_codes: str = None, + region: str = None, ): - self.model_domain_ids = model_domain_ids + self.module_codes = module_codes + self.region = region def validate(self): pass @@ -8266,31 +12219,29 @@ def to_map(self): return _map result = dict() - if self.model_domain_ids is not None: - result['ModelDomainIds'] = self.model_domain_ids + if self.module_codes is not None: + result['ModuleCodes'] = self.module_codes + if self.region is not None: + result['Region'] = self.region return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ModelDomainIds') is not None: - self.model_domain_ids = m.get('ModelDomainIds') + if m.get('ModuleCodes') is not None: + self.module_codes = m.get('ModuleCodes') + if m.get('Region') is not None: + self.region = m.get('Region') return self -class ListModelDomainsResponseBodyModelDomainsModelTasks(TeaModel): +class ListModuleConfigsResponseBodyModuleConfigsConfigs(TeaModel): def __init__( self, - model_domain_id: str = None, - model_task_id: str = None, - model_task_name: str = None, - order_number: int = None, - search_words: str = None, + key: str = None, + value: str = None, ): - self.model_domain_id = model_domain_id - self.model_task_id = model_task_id - self.model_task_name = model_task_name - self.order_number = order_number - self.search_words = search_words + self.key = key + self.value = value def validate(self): pass @@ -8301,210 +12252,37 @@ def to_map(self): return _map result = dict() - if self.model_domain_id is not None: - result['ModelDomainId'] = self.model_domain_id - if self.model_task_id is not None: - result['ModelTaskId'] = self.model_task_id - if self.model_task_name is not None: - result['ModelTaskName'] = self.model_task_name - if self.order_number is not None: - result['OrderNumber'] = self.order_number - if self.search_words is not None: - result['SearchWords'] = self.search_words - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('ModelDomainId') is not None: - self.model_domain_id = m.get('ModelDomainId') - if m.get('ModelTaskId') is not None: - self.model_task_id = m.get('ModelTaskId') - if m.get('ModelTaskName') is not None: - self.model_task_name = m.get('ModelTaskName') - if m.get('OrderNumber') is not None: - self.order_number = m.get('OrderNumber') - if m.get('SearchWords') is not None: - self.search_words = m.get('SearchWords') - return self - - -class ListModelDomainsResponseBodyModelDomains(TeaModel): - def __init__( - self, - model_domain_id: str = None, - model_domain_name: str = None, - model_tasks: List[ListModelDomainsResponseBodyModelDomainsModelTasks] = None, - order_number: int = None, - ): - self.model_domain_id = model_domain_id - self.model_domain_name = model_domain_name - self.model_tasks = model_tasks - self.order_number = order_number - - def validate(self): - if self.model_tasks: - for k in self.model_tasks: - if k: - k.validate() - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.model_domain_id is not None: - result['ModelDomainId'] = self.model_domain_id - if self.model_domain_name is not None: - result['ModelDomainName'] = self.model_domain_name - result['ModelTasks'] = [] - if self.model_tasks is not None: - for k in self.model_tasks: - result['ModelTasks'].append(k.to_map() if k else None) - if self.order_number is not None: - result['OrderNumber'] = self.order_number - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('ModelDomainId') is not None: - self.model_domain_id = m.get('ModelDomainId') - if m.get('ModelDomainName') is not None: - self.model_domain_name = m.get('ModelDomainName') - self.model_tasks = [] - if m.get('ModelTasks') is not None: - for k in m.get('ModelTasks'): - temp_model = ListModelDomainsResponseBodyModelDomainsModelTasks() - self.model_tasks.append(temp_model.from_map(k)) - if m.get('OrderNumber') is not None: - self.order_number = m.get('OrderNumber') - return self - - -class ListModelDomainsResponseBody(TeaModel): - def __init__( - self, - model_domains: List[ListModelDomainsResponseBodyModelDomains] = None, - request_id: str = None, - total_count: int = None, - ): - self.model_domains = model_domains - self.request_id = request_id - self.total_count = total_count - - def validate(self): - if self.model_domains: - for k in self.model_domains: - if k: - k.validate() - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - result['ModelDomains'] = [] - if self.model_domains is not None: - for k in self.model_domains: - result['ModelDomains'].append(k.to_map() if k else None) - if self.request_id is not None: - result['RequestId'] = self.request_id - if self.total_count is not None: - result['TotalCount'] = self.total_count - return result - - def from_map(self, m: dict = None): - m = m or dict() - self.model_domains = [] - if m.get('ModelDomains') is not None: - for k in m.get('ModelDomains'): - temp_model = ListModelDomainsResponseBodyModelDomains() - self.model_domains.append(temp_model.from_map(k)) - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - if m.get('TotalCount') is not None: - self.total_count = m.get('TotalCount') - return self - - -class ListModelDomainsResponse(TeaModel): - def __init__( - self, - headers: Dict[str, str] = None, - status_code: int = None, - body: ListModelDomainsResponseBody = None, - ): - self.headers = headers - self.status_code = status_code - self.body = body - - def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') - if self.body: - self.body.validate() - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.headers is not None: - result['headers'] = self.headers - if self.status_code is not None: - result['statusCode'] = self.status_code - if self.body is not None: - result['body'] = self.body.to_map() + if self.key is not None: + result['Key'] = self.key + if self.value is not None: + result['Value'] = self.value return result def from_map(self, m: dict = None): m = m or dict() - if m.get('headers') is not None: - self.headers = m.get('headers') - if m.get('statusCode') is not None: - self.status_code = m.get('statusCode') - if m.get('body') is not None: - temp_model = ListModelDomainsResponseBody() - self.body = temp_model.from_map(m['body']) + if m.get('Key') is not None: + self.key = m.get('Key') + if m.get('Value') is not None: + self.value = m.get('Value') return self -class ListModelVersionsRequest(TeaModel): +class ListModuleConfigsResponseBodyModuleConfigs(TeaModel): def __init__( self, - approval_status: str = None, - format_type: str = None, - framework_type: str = None, - label: str = None, - label_string: str = None, - labels: str = None, - order: str = None, - page_number: int = None, - page_size: int = None, - sort_by: str = None, - source_id: str = None, - source_type: str = None, - version_name: str = None, + configs: List[ListModuleConfigsResponseBodyModuleConfigsConfigs] = None, + module_code: str = None, + region: str = None, ): - self.approval_status = approval_status - self.format_type = format_type - self.framework_type = framework_type - self.label = label - self.label_string = label_string - self.labels = labels - self.order = order - self.page_number = page_number - self.page_size = page_size - self.sort_by = sort_by - self.source_id = source_id - self.source_type = source_type - self.version_name = version_name + self.configs = configs + self.module_code = module_code + self.region = region def validate(self): - pass + if self.configs: + for k in self.configs: + if k: + k.validate() def to_map(self): _map = super().to_map() @@ -8512,79 +12290,44 @@ def to_map(self): return _map result = dict() - if self.approval_status is not None: - result['ApprovalStatus'] = self.approval_status - if self.format_type is not None: - result['FormatType'] = self.format_type - if self.framework_type is not None: - result['FrameworkType'] = self.framework_type - if self.label is not None: - result['Label'] = self.label - if self.label_string is not None: - result['LabelString'] = self.label_string - if self.labels is not None: - result['Labels'] = self.labels - if self.order is not None: - result['Order'] = self.order - if self.page_number is not None: - result['PageNumber'] = self.page_number - if self.page_size is not None: - result['PageSize'] = self.page_size - if self.sort_by is not None: - result['SortBy'] = self.sort_by - if self.source_id is not None: - result['SourceId'] = self.source_id - if self.source_type is not None: - result['SourceType'] = self.source_type - if self.version_name is not None: - result['VersionName'] = self.version_name + result['Configs'] = [] + if self.configs is not None: + for k in self.configs: + result['Configs'].append(k.to_map() if k else None) + if self.module_code is not None: + result['ModuleCode'] = self.module_code + if self.region is not None: + result['Region'] = self.region return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ApprovalStatus') is not None: - self.approval_status = m.get('ApprovalStatus') - if m.get('FormatType') is not None: - self.format_type = m.get('FormatType') - if m.get('FrameworkType') is not None: - self.framework_type = m.get('FrameworkType') - if m.get('Label') is not None: - self.label = m.get('Label') - if m.get('LabelString') is not None: - self.label_string = m.get('LabelString') - if m.get('Labels') is not None: - self.labels = m.get('Labels') - if m.get('Order') is not None: - self.order = m.get('Order') - if m.get('PageNumber') is not None: - self.page_number = m.get('PageNumber') - if m.get('PageSize') is not None: - self.page_size = m.get('PageSize') - if m.get('SortBy') is not None: - self.sort_by = m.get('SortBy') - if m.get('SourceId') is not None: - self.source_id = m.get('SourceId') - if m.get('SourceType') is not None: - self.source_type = m.get('SourceType') - if m.get('VersionName') is not None: - self.version_name = m.get('VersionName') + self.configs = [] + if m.get('Configs') is not None: + for k in m.get('Configs'): + temp_model = ListModuleConfigsResponseBodyModuleConfigsConfigs() + self.configs.append(temp_model.from_map(k)) + if m.get('ModuleCode') is not None: + self.module_code = m.get('ModuleCode') + if m.get('Region') is not None: + self.region = m.get('Region') return self -class ListModelVersionsResponseBody(TeaModel): +class ListModuleConfigsResponseBody(TeaModel): def __init__( self, + module_configs: List[ListModuleConfigsResponseBodyModuleConfigs] = None, request_id: str = None, total_count: int = None, - versions: List[ModelVersion] = None, ): + self.module_configs = module_configs self.request_id = request_id self.total_count = total_count - self.versions = versions def validate(self): - if self.versions: - for k in self.versions: + if self.module_configs: + for k in self.module_configs: if k: k.validate() @@ -8594,45 +12337,42 @@ def to_map(self): return _map result = dict() + result['ModuleConfigs'] = [] + if self.module_configs is not None: + for k in self.module_configs: + result['ModuleConfigs'].append(k.to_map() if k else None) if self.request_id is not None: result['RequestId'] = self.request_id if self.total_count is not None: result['TotalCount'] = self.total_count - result['Versions'] = [] - if self.versions is not None: - for k in self.versions: - result['Versions'].append(k.to_map() if k else None) return result def from_map(self, m: dict = None): m = m or dict() + self.module_configs = [] + if m.get('ModuleConfigs') is not None: + for k in m.get('ModuleConfigs'): + temp_model = ListModuleConfigsResponseBodyModuleConfigs() + self.module_configs.append(temp_model.from_map(k)) if m.get('RequestId') is not None: self.request_id = m.get('RequestId') if m.get('TotalCount') is not None: self.total_count = m.get('TotalCount') - self.versions = [] - if m.get('Versions') is not None: - for k in m.get('Versions'): - temp_model = ModelVersion() - self.versions.append(temp_model.from_map(k)) return self -class ListModelVersionsResponse(TeaModel): +class ListModuleConfigsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListModelVersionsResponseBody = None, + body: ListModuleConfigsResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -8657,43 +12397,31 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListModelVersionsResponseBody() + temp_model = ListModuleConfigsResponseBody() self.body = temp_model.from_map(m['body']) return self -class ListModelsRequest(TeaModel): +class ListOperationLogsRequest(TeaModel): def __init__( self, - domain: str = None, - label: str = None, - label_string: str = None, - labels: str = None, - model_name: str = None, + entity_status: str = None, + entity_types: str = None, + operation_status: str = None, + operations: str = None, order: str = None, - origin: str = None, page_number: int = None, page_size: int = None, - provider: str = None, - query: str = None, sort_by: str = None, - task: str = None, - workspace_id: str = None, ): - self.domain = domain - self.label = label - self.label_string = label_string - self.labels = labels - self.model_name = model_name + self.entity_status = entity_status + self.entity_types = entity_types + self.operation_status = operation_status + self.operations = operations self.order = order - self.origin = origin self.page_number = page_number self.page_size = page_size - self.provider = provider - self.query = query self.sort_by = sort_by - self.task = task - self.workspace_id = workspace_id def validate(self): pass @@ -8704,83 +12432,122 @@ def to_map(self): return _map result = dict() - if self.domain is not None: - result['Domain'] = self.domain - if self.label is not None: - result['Label'] = self.label - if self.label_string is not None: - result['LabelString'] = self.label_string - if self.labels is not None: - result['Labels'] = self.labels - if self.model_name is not None: - result['ModelName'] = self.model_name + if self.entity_status is not None: + result['EntityStatus'] = self.entity_status + if self.entity_types is not None: + result['EntityTypes'] = self.entity_types + if self.operation_status is not None: + result['OperationStatus'] = self.operation_status + if self.operations is not None: + result['Operations'] = self.operations if self.order is not None: result['Order'] = self.order - if self.origin is not None: - result['Origin'] = self.origin if self.page_number is not None: result['PageNumber'] = self.page_number if self.page_size is not None: result['PageSize'] = self.page_size - if self.provider is not None: - result['Provider'] = self.provider - if self.query is not None: - result['Query'] = self.query if self.sort_by is not None: result['SortBy'] = self.sort_by - if self.task is not None: - result['Task'] = self.task - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Domain') is not None: - self.domain = m.get('Domain') - if m.get('Label') is not None: - self.label = m.get('Label') - if m.get('LabelString') is not None: - self.label_string = m.get('LabelString') - if m.get('Labels') is not None: - self.labels = m.get('Labels') - if m.get('ModelName') is not None: - self.model_name = m.get('ModelName') - if m.get('Order') is not None: - self.order = m.get('Order') - if m.get('Origin') is not None: - self.origin = m.get('Origin') - if m.get('PageNumber') is not None: - self.page_number = m.get('PageNumber') - if m.get('PageSize') is not None: - self.page_size = m.get('PageSize') - if m.get('Provider') is not None: - self.provider = m.get('Provider') - if m.get('Query') is not None: - self.query = m.get('Query') - if m.get('SortBy') is not None: - self.sort_by = m.get('SortBy') - if m.get('Task') is not None: - self.task = m.get('Task') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') + if m.get('EntityStatus') is not None: + self.entity_status = m.get('EntityStatus') + if m.get('EntityTypes') is not None: + self.entity_types = m.get('EntityTypes') + if m.get('OperationStatus') is not None: + self.operation_status = m.get('OperationStatus') + if m.get('Operations') is not None: + self.operations = m.get('Operations') + if m.get('Order') is not None: + self.order = m.get('Order') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + if m.get('SortBy') is not None: + self.sort_by = m.get('SortBy') + return self + + +class ListOperationLogsResponseBodyLogs(TeaModel): + def __init__( + self, + entity_id: str = None, + entity_type: str = None, + gmt_create_time: str = None, + message: str = None, + operation: str = None, + operation_status: str = None, + operator: str = None, + ): + self.entity_id = entity_id + self.entity_type = entity_type + self.gmt_create_time = gmt_create_time + self.message = message + self.operation = operation + self.operation_status = operation_status + self.operator = operator + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.entity_id is not None: + result['EntityId'] = self.entity_id + if self.entity_type is not None: + result['EntityType'] = self.entity_type + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.message is not None: + result['Message'] = self.message + if self.operation is not None: + result['Operation'] = self.operation + if self.operation_status is not None: + result['OperationStatus'] = self.operation_status + if self.operator is not None: + result['Operator'] = self.operator + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('EntityId') is not None: + self.entity_id = m.get('EntityId') + if m.get('EntityType') is not None: + self.entity_type = m.get('EntityType') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('Message') is not None: + self.message = m.get('Message') + if m.get('Operation') is not None: + self.operation = m.get('Operation') + if m.get('OperationStatus') is not None: + self.operation_status = m.get('OperationStatus') + if m.get('Operator') is not None: + self.operator = m.get('Operator') return self -class ListModelsResponseBody(TeaModel): +class ListOperationLogsResponseBody(TeaModel): def __init__( self, - models: List[Model] = None, + logs: List[ListOperationLogsResponseBodyLogs] = None, request_id: str = None, total_count: int = None, ): - self.models = models + self.logs = logs self.request_id = request_id self.total_count = total_count def validate(self): - if self.models: - for k in self.models: + if self.logs: + for k in self.logs: if k: k.validate() @@ -8790,10 +12557,10 @@ def to_map(self): return _map result = dict() - result['Models'] = [] - if self.models is not None: - for k in self.models: - result['Models'].append(k.to_map() if k else None) + result['Logs'] = [] + if self.logs is not None: + for k in self.logs: + result['Logs'].append(k.to_map() if k else None) if self.request_id is not None: result['RequestId'] = self.request_id if self.total_count is not None: @@ -8802,11 +12569,11 @@ def to_map(self): def from_map(self, m: dict = None): m = m or dict() - self.models = [] - if m.get('Models') is not None: - for k in m.get('Models'): - temp_model = Model() - self.models.append(temp_model.from_map(k)) + self.logs = [] + if m.get('Logs') is not None: + for k in m.get('Logs'): + temp_model = ListOperationLogsResponseBodyLogs() + self.logs.append(temp_model.from_map(k)) if m.get('RequestId') is not None: self.request_id = m.get('RequestId') if m.get('TotalCount') is not None: @@ -8814,21 +12581,18 @@ def from_map(self, m: dict = None): return self -class ListModelsResponse(TeaModel): +class ListOperationLogsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListModelsResponseBody = None, + body: ListOperationLogsResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -8853,52 +12617,19 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListModelsResponseBody() + temp_model = ListOperationLogsResponseBody() self.body = temp_model.from_map(m['body']) return self -class ListModuleConfigsRequest(TeaModel): - def __init__( - self, - module_codes: str = None, - region: str = None, - ): - self.module_codes = module_codes - self.region = region - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.module_codes is not None: - result['ModuleCodes'] = self.module_codes - if self.region is not None: - result['Region'] = self.region - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('ModuleCodes') is not None: - self.module_codes = m.get('ModuleCodes') - if m.get('Region') is not None: - self.region = m.get('Region') - return self - - -class ListModuleConfigsResponseBodyModuleConfigsConfigs(TeaModel): +class ListPermissionsResponseBodyPermissionsPermissionRules(TeaModel): def __init__( self, - key: str = None, - value: str = None, + accessibility: str = None, + entity_access_type: str = None, ): - self.key = key - self.value = value + self.accessibility = accessibility + self.entity_access_type = entity_access_type def validate(self): pass @@ -8909,35 +12640,33 @@ def to_map(self): return _map result = dict() - if self.key is not None: - result['Key'] = self.key - if self.value is not None: - result['Value'] = self.value + if self.accessibility is not None: + result['Accessibility'] = self.accessibility + if self.entity_access_type is not None: + result['EntityAccessType'] = self.entity_access_type return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Key') is not None: - self.key = m.get('Key') - if m.get('Value') is not None: - self.value = m.get('Value') + if m.get('Accessibility') is not None: + self.accessibility = m.get('Accessibility') + if m.get('EntityAccessType') is not None: + self.entity_access_type = m.get('EntityAccessType') return self -class ListModuleConfigsResponseBodyModuleConfigs(TeaModel): +class ListPermissionsResponseBodyPermissions(TeaModel): def __init__( self, - configs: List[ListModuleConfigsResponseBodyModuleConfigsConfigs] = None, - module_code: str = None, - region: str = None, + permission_code: str = None, + permission_rules: List[ListPermissionsResponseBodyPermissionsPermissionRules] = None, ): - self.configs = configs - self.module_code = module_code - self.region = region + self.permission_code = permission_code + self.permission_rules = permission_rules def validate(self): - if self.configs: - for k in self.configs: + if self.permission_rules: + for k in self.permission_rules: if k: k.validate() @@ -8947,44 +12676,40 @@ def to_map(self): return _map result = dict() - result['Configs'] = [] - if self.configs is not None: - for k in self.configs: - result['Configs'].append(k.to_map() if k else None) - if self.module_code is not None: - result['ModuleCode'] = self.module_code - if self.region is not None: - result['Region'] = self.region + if self.permission_code is not None: + result['PermissionCode'] = self.permission_code + result['PermissionRules'] = [] + if self.permission_rules is not None: + for k in self.permission_rules: + result['PermissionRules'].append(k.to_map() if k else None) return result def from_map(self, m: dict = None): m = m or dict() - self.configs = [] - if m.get('Configs') is not None: - for k in m.get('Configs'): - temp_model = ListModuleConfigsResponseBodyModuleConfigsConfigs() - self.configs.append(temp_model.from_map(k)) - if m.get('ModuleCode') is not None: - self.module_code = m.get('ModuleCode') - if m.get('Region') is not None: - self.region = m.get('Region') + if m.get('PermissionCode') is not None: + self.permission_code = m.get('PermissionCode') + self.permission_rules = [] + if m.get('PermissionRules') is not None: + for k in m.get('PermissionRules'): + temp_model = ListPermissionsResponseBodyPermissionsPermissionRules() + self.permission_rules.append(temp_model.from_map(k)) return self -class ListModuleConfigsResponseBody(TeaModel): +class ListPermissionsResponseBody(TeaModel): def __init__( self, - module_configs: List[ListModuleConfigsResponseBodyModuleConfigs] = None, + permissions: List[ListPermissionsResponseBodyPermissions] = None, request_id: str = None, total_count: int = None, ): - self.module_configs = module_configs + self.permissions = permissions self.request_id = request_id self.total_count = total_count def validate(self): - if self.module_configs: - for k in self.module_configs: + if self.permissions: + for k in self.permissions: if k: k.validate() @@ -8994,10 +12719,10 @@ def to_map(self): return _map result = dict() - result['ModuleConfigs'] = [] - if self.module_configs is not None: - for k in self.module_configs: - result['ModuleConfigs'].append(k.to_map() if k else None) + result['Permissions'] = [] + if self.permissions is not None: + for k in self.permissions: + result['Permissions'].append(k.to_map() if k else None) if self.request_id is not None: result['RequestId'] = self.request_id if self.total_count is not None: @@ -9006,11 +12731,11 @@ def to_map(self): def from_map(self, m: dict = None): m = m or dict() - self.module_configs = [] - if m.get('ModuleConfigs') is not None: - for k in m.get('ModuleConfigs'): - temp_model = ListModuleConfigsResponseBodyModuleConfigs() - self.module_configs.append(temp_model.from_map(k)) + self.permissions = [] + if m.get('Permissions') is not None: + for k in m.get('Permissions'): + temp_model = ListPermissionsResponseBodyPermissions() + self.permissions.append(temp_model.from_map(k)) if m.get('RequestId') is not None: self.request_id = m.get('RequestId') if m.get('TotalCount') is not None: @@ -9018,21 +12743,18 @@ def from_map(self, m: dict = None): return self -class ListModuleConfigsResponse(TeaModel): +class ListPermissionsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListModuleConfigsResponseBody = None, + body: ListPermissionsResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -9057,31 +12779,17 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListModuleConfigsResponseBody() + temp_model = ListPermissionsResponseBody() self.body = temp_model.from_map(m['body']) return self -class ListOperationLogsRequest(TeaModel): +class ListProductAuthorizationsRequest(TeaModel): def __init__( self, - entity_status: str = None, - entity_types: str = None, - operation_status: str = None, - operations: str = None, - order: str = None, - page_number: int = None, - page_size: int = None, - sort_by: str = None, + ram_role_names: str = None, ): - self.entity_status = entity_status - self.entity_types = entity_types - self.operation_status = operation_status - self.operations = operations - self.order = order - self.page_number = page_number - self.page_size = page_size - self.sort_by = sort_by + self.ram_role_names = ram_role_names def validate(self): pass @@ -9092,63 +12800,31 @@ def to_map(self): return _map result = dict() - if self.entity_status is not None: - result['EntityStatus'] = self.entity_status - if self.entity_types is not None: - result['EntityTypes'] = self.entity_types - if self.operation_status is not None: - result['OperationStatus'] = self.operation_status - if self.operations is not None: - result['Operations'] = self.operations - if self.order is not None: - result['Order'] = self.order - if self.page_number is not None: - result['PageNumber'] = self.page_number - if self.page_size is not None: - result['PageSize'] = self.page_size - if self.sort_by is not None: - result['SortBy'] = self.sort_by + if self.ram_role_names is not None: + result['RamRoleNames'] = self.ram_role_names return result def from_map(self, m: dict = None): m = m or dict() - if m.get('EntityStatus') is not None: - self.entity_status = m.get('EntityStatus') - if m.get('EntityTypes') is not None: - self.entity_types = m.get('EntityTypes') - if m.get('OperationStatus') is not None: - self.operation_status = m.get('OperationStatus') - if m.get('Operations') is not None: - self.operations = m.get('Operations') - if m.get('Order') is not None: - self.order = m.get('Order') - if m.get('PageNumber') is not None: - self.page_number = m.get('PageNumber') - if m.get('PageSize') is not None: - self.page_size = m.get('PageSize') - if m.get('SortBy') is not None: - self.sort_by = m.get('SortBy') + if m.get('RamRoleNames') is not None: + self.ram_role_names = m.get('RamRoleNames') return self -class ListOperationLogsResponseBodyLogs(TeaModel): +class ListProductAuthorizationsResponseBodyAuthorizationDetails(TeaModel): def __init__( self, - entity_id: str = None, - entity_type: str = None, - gmt_create_time: str = None, - message: str = None, - operation: str = None, - operation_status: str = None, - operator: str = None, + authorization_url: str = None, + is_authorized: bool = None, + ram_role_arn: str = None, + ram_role_name: str = None, + ram_role_type: str = None, ): - self.entity_id = entity_id - self.entity_type = entity_type - self.gmt_create_time = gmt_create_time - self.message = message - self.operation = operation - self.operation_status = operation_status - self.operator = operator + self.authorization_url = authorization_url + self.is_authorized = is_authorized + self.ram_role_arn = ram_role_arn + self.ram_role_name = ram_role_name + self.ram_role_type = ram_role_type def validate(self): pass @@ -9159,55 +12835,47 @@ def to_map(self): return _map result = dict() - if self.entity_id is not None: - result['EntityId'] = self.entity_id - if self.entity_type is not None: - result['EntityType'] = self.entity_type - if self.gmt_create_time is not None: - result['GmtCreateTime'] = self.gmt_create_time - if self.message is not None: - result['Message'] = self.message - if self.operation is not None: - result['Operation'] = self.operation - if self.operation_status is not None: - result['OperationStatus'] = self.operation_status - if self.operator is not None: - result['Operator'] = self.operator + if self.authorization_url is not None: + result['AuthorizationUrl'] = self.authorization_url + if self.is_authorized is not None: + result['IsAuthorized'] = self.is_authorized + if self.ram_role_arn is not None: + result['RamRoleARN'] = self.ram_role_arn + if self.ram_role_name is not None: + result['RamRoleName'] = self.ram_role_name + if self.ram_role_type is not None: + result['RamRoleType'] = self.ram_role_type return result def from_map(self, m: dict = None): m = m or dict() - if m.get('EntityId') is not None: - self.entity_id = m.get('EntityId') - if m.get('EntityType') is not None: - self.entity_type = m.get('EntityType') - if m.get('GmtCreateTime') is not None: - self.gmt_create_time = m.get('GmtCreateTime') - if m.get('Message') is not None: - self.message = m.get('Message') - if m.get('Operation') is not None: - self.operation = m.get('Operation') - if m.get('OperationStatus') is not None: - self.operation_status = m.get('OperationStatus') - if m.get('Operator') is not None: - self.operator = m.get('Operator') + if m.get('AuthorizationUrl') is not None: + self.authorization_url = m.get('AuthorizationUrl') + if m.get('IsAuthorized') is not None: + self.is_authorized = m.get('IsAuthorized') + if m.get('RamRoleARN') is not None: + self.ram_role_arn = m.get('RamRoleARN') + if m.get('RamRoleName') is not None: + self.ram_role_name = m.get('RamRoleName') + if m.get('RamRoleType') is not None: + self.ram_role_type = m.get('RamRoleType') return self -class ListOperationLogsResponseBody(TeaModel): +class ListProductAuthorizationsResponseBody(TeaModel): def __init__( self, - logs: List[ListOperationLogsResponseBodyLogs] = None, + authorization_details: List[ListProductAuthorizationsResponseBodyAuthorizationDetails] = None, + authorization_url: str = None, request_id: str = None, - total_count: int = None, ): - self.logs = logs + self.authorization_details = authorization_details + self.authorization_url = authorization_url self.request_id = request_id - self.total_count = total_count def validate(self): - if self.logs: - for k in self.logs: + if self.authorization_details: + for k in self.authorization_details: if k: k.validate() @@ -9217,45 +12885,42 @@ def to_map(self): return _map result = dict() - result['Logs'] = [] - if self.logs is not None: - for k in self.logs: - result['Logs'].append(k.to_map() if k else None) + result['AuthorizationDetails'] = [] + if self.authorization_details is not None: + for k in self.authorization_details: + result['AuthorizationDetails'].append(k.to_map() if k else None) + if self.authorization_url is not None: + result['AuthorizationUrl'] = self.authorization_url if self.request_id is not None: result['RequestId'] = self.request_id - if self.total_count is not None: - result['TotalCount'] = self.total_count return result def from_map(self, m: dict = None): m = m or dict() - self.logs = [] - if m.get('Logs') is not None: - for k in m.get('Logs'): - temp_model = ListOperationLogsResponseBodyLogs() - self.logs.append(temp_model.from_map(k)) + self.authorization_details = [] + if m.get('AuthorizationDetails') is not None: + for k in m.get('AuthorizationDetails'): + temp_model = ListProductAuthorizationsResponseBodyAuthorizationDetails() + self.authorization_details.append(temp_model.from_map(k)) + if m.get('AuthorizationUrl') is not None: + self.authorization_url = m.get('AuthorizationUrl') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('TotalCount') is not None: - self.total_count = m.get('TotalCount') return self -class ListOperationLogsResponse(TeaModel): +class ListProductAuthorizationsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListOperationLogsResponseBody = None, + body: ListProductAuthorizationsResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -9280,19 +12945,21 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListOperationLogsResponseBody() + temp_model = ListProductAuthorizationsResponseBody() self.body = temp_model.from_map(m['body']) return self -class ListPermissionsResponseBodyPermissionsPermissionRules(TeaModel): +class ListProductsRequest(TeaModel): def __init__( self, - accessibility: str = None, - entity_access_type: str = None, + product_codes: str = None, + service_codes: str = None, + verbose: bool = None, ): - self.accessibility = accessibility - self.entity_access_type = entity_access_type + self.product_codes = product_codes + self.service_codes = service_codes + self.verbose = verbose def validate(self): pass @@ -9303,35 +12970,89 @@ def to_map(self): return _map result = dict() - if self.accessibility is not None: - result['Accessibility'] = self.accessibility - if self.entity_access_type is not None: - result['EntityAccessType'] = self.entity_access_type + if self.product_codes is not None: + result['ProductCodes'] = self.product_codes + if self.service_codes is not None: + result['ServiceCodes'] = self.service_codes + if self.verbose is not None: + result['Verbose'] = self.verbose return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Accessibility') is not None: - self.accessibility = m.get('Accessibility') - if m.get('EntityAccessType') is not None: - self.entity_access_type = m.get('EntityAccessType') + if m.get('ProductCodes') is not None: + self.product_codes = m.get('ProductCodes') + if m.get('ServiceCodes') is not None: + self.service_codes = m.get('ServiceCodes') + if m.get('Verbose') is not None: + self.verbose = m.get('Verbose') return self -class ListPermissionsResponseBodyPermissions(TeaModel): +class ListProductsResponseBodyProducts(TeaModel): def __init__( self, - permission_code: str = None, - permission_rules: List[ListPermissionsResponseBodyPermissionsPermissionRules] = None, + has_permission_to_purchase: bool = None, + is_purchased: bool = None, + product_code: str = None, + product_instance_id: str = None, + purchase_url: str = None, ): - self.permission_code = permission_code - self.permission_rules = permission_rules + self.has_permission_to_purchase = has_permission_to_purchase + self.is_purchased = is_purchased + self.product_code = product_code + self.product_instance_id = product_instance_id + self.purchase_url = purchase_url def validate(self): - if self.permission_rules: - for k in self.permission_rules: - if k: - k.validate() + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.has_permission_to_purchase is not None: + result['HasPermissionToPurchase'] = self.has_permission_to_purchase + if self.is_purchased is not None: + result['IsPurchased'] = self.is_purchased + if self.product_code is not None: + result['ProductCode'] = self.product_code + if self.product_instance_id is not None: + result['ProductInstanceId'] = self.product_instance_id + if self.purchase_url is not None: + result['PurchaseUrl'] = self.purchase_url + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('HasPermissionToPurchase') is not None: + self.has_permission_to_purchase = m.get('HasPermissionToPurchase') + if m.get('IsPurchased') is not None: + self.is_purchased = m.get('IsPurchased') + if m.get('ProductCode') is not None: + self.product_code = m.get('ProductCode') + if m.get('ProductInstanceId') is not None: + self.product_instance_id = m.get('ProductInstanceId') + if m.get('PurchaseUrl') is not None: + self.purchase_url = m.get('PurchaseUrl') + return self + + +class ListProductsResponseBodyServices(TeaModel): + def __init__( + self, + is_open: bool = None, + open_url: str = None, + service_code: str = None, + ): + self.is_open = is_open + self.open_url = open_url + self.service_code = service_code + + def validate(self): + pass def to_map(self): _map = super().to_map() @@ -9339,40 +13060,43 @@ def to_map(self): return _map result = dict() - if self.permission_code is not None: - result['PermissionCode'] = self.permission_code - result['PermissionRules'] = [] - if self.permission_rules is not None: - for k in self.permission_rules: - result['PermissionRules'].append(k.to_map() if k else None) + if self.is_open is not None: + result['IsOpen'] = self.is_open + if self.open_url is not None: + result['OpenUrl'] = self.open_url + if self.service_code is not None: + result['ServiceCode'] = self.service_code return result def from_map(self, m: dict = None): m = m or dict() - if m.get('PermissionCode') is not None: - self.permission_code = m.get('PermissionCode') - self.permission_rules = [] - if m.get('PermissionRules') is not None: - for k in m.get('PermissionRules'): - temp_model = ListPermissionsResponseBodyPermissionsPermissionRules() - self.permission_rules.append(temp_model.from_map(k)) + if m.get('IsOpen') is not None: + self.is_open = m.get('IsOpen') + if m.get('OpenUrl') is not None: + self.open_url = m.get('OpenUrl') + if m.get('ServiceCode') is not None: + self.service_code = m.get('ServiceCode') return self -class ListPermissionsResponseBody(TeaModel): +class ListProductsResponseBody(TeaModel): def __init__( self, - permissions: List[ListPermissionsResponseBodyPermissions] = None, + products: List[ListProductsResponseBodyProducts] = None, request_id: str = None, - total_count: int = None, + services: List[ListProductsResponseBodyServices] = None, ): - self.permissions = permissions + self.products = products self.request_id = request_id - self.total_count = total_count + self.services = services def validate(self): - if self.permissions: - for k in self.permissions: + if self.products: + for k in self.products: + if k: + k.validate() + if self.services: + for k in self.services: if k: k.validate() @@ -9382,45 +13106,47 @@ def to_map(self): return _map result = dict() - result['Permissions'] = [] - if self.permissions is not None: - for k in self.permissions: - result['Permissions'].append(k.to_map() if k else None) + result['Products'] = [] + if self.products is not None: + for k in self.products: + result['Products'].append(k.to_map() if k else None) if self.request_id is not None: result['RequestId'] = self.request_id - if self.total_count is not None: - result['TotalCount'] = self.total_count + result['Services'] = [] + if self.services is not None: + for k in self.services: + result['Services'].append(k.to_map() if k else None) return result def from_map(self, m: dict = None): m = m or dict() - self.permissions = [] - if m.get('Permissions') is not None: - for k in m.get('Permissions'): - temp_model = ListPermissionsResponseBodyPermissions() - self.permissions.append(temp_model.from_map(k)) + self.products = [] + if m.get('Products') is not None: + for k in m.get('Products'): + temp_model = ListProductsResponseBodyProducts() + self.products.append(temp_model.from_map(k)) if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('TotalCount') is not None: - self.total_count = m.get('TotalCount') + self.services = [] + if m.get('Services') is not None: + for k in m.get('Services'): + temp_model = ListProductsResponseBodyServices() + self.services.append(temp_model.from_map(k)) return self -class ListPermissionsResponse(TeaModel): +class ListProductsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListPermissionsResponseBody = None, + body: ListProductsResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -9445,17 +13171,17 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListPermissionsResponseBody() + temp_model = ListProductsResponseBody() self.body = temp_model.from_map(m['body']) return self -class ListProductAuthorizationsRequest(TeaModel): +class ListQuotasRequest(TeaModel): def __init__( self, - ram_role_names: str = None, + name: str = None, ): - self.ram_role_names = ram_role_names + self.name = name def validate(self): pass @@ -9466,31 +13192,27 @@ def to_map(self): return _map result = dict() - if self.ram_role_names is not None: - result['RamRoleNames'] = self.ram_role_names + if self.name is not None: + result['Name'] = self.name return result def from_map(self, m: dict = None): m = m or dict() - if m.get('RamRoleNames') is not None: - self.ram_role_names = m.get('RamRoleNames') + if m.get('Name') is not None: + self.name = m.get('Name') return self -class ListProductAuthorizationsResponseBodyAuthorizationDetails(TeaModel): +class ListQuotasResponseBodyQuotasSpecs(TeaModel): def __init__( self, - authorization_url: str = None, - is_authorized: bool = None, - ram_role_arn: str = None, - ram_role_name: str = None, - ram_role_type: str = None, + name: str = None, + type: str = None, + value: str = None, ): - self.authorization_url = authorization_url - self.is_authorized = is_authorized - self.ram_role_arn = ram_role_arn - self.ram_role_name = ram_role_name - self.ram_role_type = ram_role_type + self.name = name + self.type = type + self.value = value def validate(self): pass @@ -9501,47 +13223,110 @@ def to_map(self): return _map result = dict() - if self.authorization_url is not None: - result['AuthorizationUrl'] = self.authorization_url - if self.is_authorized is not None: - result['IsAuthorized'] = self.is_authorized - if self.ram_role_arn is not None: - result['RamRoleARN'] = self.ram_role_arn - if self.ram_role_name is not None: - result['RamRoleName'] = self.ram_role_name - if self.ram_role_type is not None: - result['RamRoleType'] = self.ram_role_type + if self.name is not None: + result['Name'] = self.name + if self.type is not None: + result['Type'] = self.type + if self.value is not None: + result['Value'] = self.value return result def from_map(self, m: dict = None): m = m or dict() - if m.get('AuthorizationUrl') is not None: - self.authorization_url = m.get('AuthorizationUrl') - if m.get('IsAuthorized') is not None: - self.is_authorized = m.get('IsAuthorized') - if m.get('RamRoleARN') is not None: - self.ram_role_arn = m.get('RamRoleARN') - if m.get('RamRoleName') is not None: - self.ram_role_name = m.get('RamRoleName') - if m.get('RamRoleType') is not None: - self.ram_role_type = m.get('RamRoleType') + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('Type') is not None: + self.type = m.get('Type') + if m.get('Value') is not None: + self.value = m.get('Value') return self -class ListProductAuthorizationsResponseBody(TeaModel): +class ListQuotasResponseBodyQuotas(TeaModel): def __init__( self, - authorization_details: List[ListProductAuthorizationsResponseBodyAuthorizationDetails] = None, - authorization_url: str = None, + display_name: str = None, + id: str = None, + mode: str = None, + name: str = None, + product_code: str = None, + quota_type: str = None, + specs: List[ListQuotasResponseBodyQuotasSpecs] = None, + ): + self.display_name = display_name + self.id = id + self.mode = mode + self.name = name + self.product_code = product_code + self.quota_type = quota_type + self.specs = specs + + def validate(self): + if self.specs: + for k in self.specs: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.display_name is not None: + result['DisplayName'] = self.display_name + if self.id is not None: + result['Id'] = self.id + if self.mode is not None: + result['Mode'] = self.mode + if self.name is not None: + result['Name'] = self.name + if self.product_code is not None: + result['ProductCode'] = self.product_code + if self.quota_type is not None: + result['QuotaType'] = self.quota_type + result['Specs'] = [] + if self.specs is not None: + for k in self.specs: + result['Specs'].append(k.to_map() if k else None) + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('DisplayName') is not None: + self.display_name = m.get('DisplayName') + if m.get('Id') is not None: + self.id = m.get('Id') + if m.get('Mode') is not None: + self.mode = m.get('Mode') + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('ProductCode') is not None: + self.product_code = m.get('ProductCode') + if m.get('QuotaType') is not None: + self.quota_type = m.get('QuotaType') + self.specs = [] + if m.get('Specs') is not None: + for k in m.get('Specs'): + temp_model = ListQuotasResponseBodyQuotasSpecs() + self.specs.append(temp_model.from_map(k)) + return self + + +class ListQuotasResponseBody(TeaModel): + def __init__( + self, + quotas: List[ListQuotasResponseBodyQuotas] = None, request_id: str = None, + total_count: int = None, ): - self.authorization_details = authorization_details - self.authorization_url = authorization_url + self.quotas = quotas self.request_id = request_id + self.total_count = total_count def validate(self): - if self.authorization_details: - for k in self.authorization_details: + if self.quotas: + for k in self.quotas: if k: k.validate() @@ -9551,45 +13336,42 @@ def to_map(self): return _map result = dict() - result['AuthorizationDetails'] = [] - if self.authorization_details is not None: - for k in self.authorization_details: - result['AuthorizationDetails'].append(k.to_map() if k else None) - if self.authorization_url is not None: - result['AuthorizationUrl'] = self.authorization_url + result['Quotas'] = [] + if self.quotas is not None: + for k in self.quotas: + result['Quotas'].append(k.to_map() if k else None) if self.request_id is not None: result['RequestId'] = self.request_id + if self.total_count is not None: + result['TotalCount'] = self.total_count return result def from_map(self, m: dict = None): m = m or dict() - self.authorization_details = [] - if m.get('AuthorizationDetails') is not None: - for k in m.get('AuthorizationDetails'): - temp_model = ListProductAuthorizationsResponseBodyAuthorizationDetails() - self.authorization_details.append(temp_model.from_map(k)) - if m.get('AuthorizationUrl') is not None: - self.authorization_url = m.get('AuthorizationUrl') + self.quotas = [] + if m.get('Quotas') is not None: + for k in m.get('Quotas'): + temp_model = ListQuotasResponseBodyQuotas() + self.quotas.append(temp_model.from_map(k)) if m.get('RequestId') is not None: self.request_id = m.get('RequestId') + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') return self -class ListProductAuthorizationsResponse(TeaModel): +class ListQuotasResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListProductAuthorizationsResponseBody = None, + body: ListQuotasResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -9614,21 +13396,39 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListProductAuthorizationsResponseBody() + temp_model = ListQuotasResponseBody() self.body = temp_model.from_map(m['body']) return self -class ListProductsRequest(TeaModel): +class ListResourcesRequest(TeaModel): def __init__( self, - product_codes: str = None, - service_codes: str = None, + group_name: str = None, + labels: str = None, + option: str = None, + page_number: int = None, + page_size: int = None, + product_types: str = None, + quota_ids: str = None, + resource_name: str = None, + resource_types: str = None, verbose: bool = None, + verbose_fields: str = None, + workspace_id: str = None, ): - self.product_codes = product_codes - self.service_codes = service_codes + self.group_name = group_name + self.labels = labels + self.option = option + self.page_number = page_number + self.page_size = page_size + self.product_types = product_types + self.quota_ids = quota_ids + self.resource_name = resource_name + self.resource_types = resource_types self.verbose = verbose + self.verbose_fields = verbose_fields + self.workspace_id = workspace_id def validate(self): pass @@ -9639,39 +13439,71 @@ def to_map(self): return _map result = dict() - if self.product_codes is not None: - result['ProductCodes'] = self.product_codes - if self.service_codes is not None: - result['ServiceCodes'] = self.service_codes + if self.group_name is not None: + result['GroupName'] = self.group_name + if self.labels is not None: + result['Labels'] = self.labels + if self.option is not None: + result['Option'] = self.option + if self.page_number is not None: + result['PageNumber'] = self.page_number + if self.page_size is not None: + result['PageSize'] = self.page_size + if self.product_types is not None: + result['ProductTypes'] = self.product_types + if self.quota_ids is not None: + result['QuotaIds'] = self.quota_ids + if self.resource_name is not None: + result['ResourceName'] = self.resource_name + if self.resource_types is not None: + result['ResourceTypes'] = self.resource_types if self.verbose is not None: result['Verbose'] = self.verbose + if self.verbose_fields is not None: + result['VerboseFields'] = self.verbose_fields + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ProductCodes') is not None: - self.product_codes = m.get('ProductCodes') - if m.get('ServiceCodes') is not None: - self.service_codes = m.get('ServiceCodes') + if m.get('GroupName') is not None: + self.group_name = m.get('GroupName') + if m.get('Labels') is not None: + self.labels = m.get('Labels') + if m.get('Option') is not None: + self.option = m.get('Option') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + if m.get('ProductTypes') is not None: + self.product_types = m.get('ProductTypes') + if m.get('QuotaIds') is not None: + self.quota_ids = m.get('QuotaIds') + if m.get('ResourceName') is not None: + self.resource_name = m.get('ResourceName') + if m.get('ResourceTypes') is not None: + self.resource_types = m.get('ResourceTypes') if m.get('Verbose') is not None: self.verbose = m.get('Verbose') + if m.get('VerboseFields') is not None: + self.verbose_fields = m.get('VerboseFields') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class ListProductsResponseBodyProducts(TeaModel): +class ListResourcesResponseBodyResourcesEncryption(TeaModel): def __init__( self, - has_permission_to_purchase: bool = None, - is_purchased: bool = None, - product_code: str = None, - product_instance_id: str = None, - purchase_url: str = None, + algorithm: str = None, + enabled: bool = None, + key: str = None, ): - self.has_permission_to_purchase = has_permission_to_purchase - self.is_purchased = is_purchased - self.product_code = product_code - self.product_instance_id = product_instance_id - self.purchase_url = purchase_url + self.algorithm = algorithm + self.enabled = enabled + self.key = key def validate(self): pass @@ -9682,43 +13514,31 @@ def to_map(self): return _map result = dict() - if self.has_permission_to_purchase is not None: - result['HasPermissionToPurchase'] = self.has_permission_to_purchase - if self.is_purchased is not None: - result['IsPurchased'] = self.is_purchased - if self.product_code is not None: - result['ProductCode'] = self.product_code - if self.product_instance_id is not None: - result['ProductInstanceId'] = self.product_instance_id - if self.purchase_url is not None: - result['PurchaseUrl'] = self.purchase_url + if self.algorithm is not None: + result['Algorithm'] = self.algorithm + if self.enabled is not None: + result['Enabled'] = self.enabled + if self.key is not None: + result['Key'] = self.key return result def from_map(self, m: dict = None): m = m or dict() - if m.get('HasPermissionToPurchase') is not None: - self.has_permission_to_purchase = m.get('HasPermissionToPurchase') - if m.get('IsPurchased') is not None: - self.is_purchased = m.get('IsPurchased') - if m.get('ProductCode') is not None: - self.product_code = m.get('ProductCode') - if m.get('ProductInstanceId') is not None: - self.product_instance_id = m.get('ProductInstanceId') - if m.get('PurchaseUrl') is not None: - self.purchase_url = m.get('PurchaseUrl') + if m.get('Algorithm') is not None: + self.algorithm = m.get('Algorithm') + if m.get('Enabled') is not None: + self.enabled = m.get('Enabled') + if m.get('Key') is not None: + self.key = m.get('Key') return self -class ListProductsResponseBodyServices(TeaModel): +class ListResourcesResponseBodyResourcesExecutor(TeaModel): def __init__( self, - is_open: bool = None, - open_url: str = None, - service_code: str = None, + owner_id: str = None, ): - self.is_open = is_open - self.open_url = open_url - self.service_code = service_code + self.owner_id = owner_id def validate(self): pass @@ -9729,131 +13549,25 @@ def to_map(self): return _map result = dict() - if self.is_open is not None: - result['IsOpen'] = self.is_open - if self.open_url is not None: - result['OpenUrl'] = self.open_url - if self.service_code is not None: - result['ServiceCode'] = self.service_code - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('IsOpen') is not None: - self.is_open = m.get('IsOpen') - if m.get('OpenUrl') is not None: - self.open_url = m.get('OpenUrl') - if m.get('ServiceCode') is not None: - self.service_code = m.get('ServiceCode') - return self - - -class ListProductsResponseBody(TeaModel): - def __init__( - self, - products: List[ListProductsResponseBodyProducts] = None, - request_id: str = None, - services: List[ListProductsResponseBodyServices] = None, - ): - self.products = products - self.request_id = request_id - self.services = services - - def validate(self): - if self.products: - for k in self.products: - if k: - k.validate() - if self.services: - for k in self.services: - if k: - k.validate() - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - result['Products'] = [] - if self.products is not None: - for k in self.products: - result['Products'].append(k.to_map() if k else None) - if self.request_id is not None: - result['RequestId'] = self.request_id - result['Services'] = [] - if self.services is not None: - for k in self.services: - result['Services'].append(k.to_map() if k else None) - return result - - def from_map(self, m: dict = None): - m = m or dict() - self.products = [] - if m.get('Products') is not None: - for k in m.get('Products'): - temp_model = ListProductsResponseBodyProducts() - self.products.append(temp_model.from_map(k)) - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - self.services = [] - if m.get('Services') is not None: - for k in m.get('Services'): - temp_model = ListProductsResponseBodyServices() - self.services.append(temp_model.from_map(k)) - return self - - -class ListProductsResponse(TeaModel): - def __init__( - self, - headers: Dict[str, str] = None, - status_code: int = None, - body: ListProductsResponseBody = None, - ): - self.headers = headers - self.status_code = status_code - self.body = body - - def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') - if self.body: - self.body.validate() - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.headers is not None: - result['headers'] = self.headers - if self.status_code is not None: - result['statusCode'] = self.status_code - if self.body is not None: - result['body'] = self.body.to_map() + if self.owner_id is not None: + result['OwnerId'] = self.owner_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('headers') is not None: - self.headers = m.get('headers') - if m.get('statusCode') is not None: - self.status_code = m.get('statusCode') - if m.get('body') is not None: - temp_model = ListProductsResponseBody() - self.body = temp_model.from_map(m['body']) + if m.get('OwnerId') is not None: + self.owner_id = m.get('OwnerId') return self -class ListQuotasRequest(TeaModel): +class ListResourcesResponseBodyResourcesLabels(TeaModel): def __init__( self, - name: str = None, + key: str = None, + value: str = None, ): - self.name = name + self.key = key + self.value = value def validate(self): pass @@ -9864,26 +13578,28 @@ def to_map(self): return _map result = dict() - if self.name is not None: - result['Name'] = self.name + if self.key is not None: + result['Key'] = self.key + if self.value is not None: + result['Value'] = self.value return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Name') is not None: - self.name = m.get('Name') + if m.get('Key') is not None: + self.key = m.get('Key') + if m.get('Value') is not None: + self.value = m.get('Value') return self -class ListQuotasResponseBodyQuotasSpecs(TeaModel): +class ListResourcesResponseBodyResourcesQuotasSpecs(TeaModel): def __init__( self, name: str = None, - type: str = None, value: str = None, ): self.name = name - self.type = type self.value = value def validate(self): @@ -9897,8 +13613,6 @@ def to_map(self): result = dict() if self.name is not None: result['Name'] = self.name - if self.type is not None: - result['Type'] = self.type if self.value is not None: result['Value'] = self.value return result @@ -9907,24 +13621,24 @@ def from_map(self, m: dict = None): m = m or dict() if m.get('Name') is not None: self.name = m.get('Name') - if m.get('Type') is not None: - self.type = m.get('Type') if m.get('Value') is not None: self.value = m.get('Value') return self -class ListQuotasResponseBodyQuotas(TeaModel): +class ListResourcesResponseBodyResourcesQuotas(TeaModel): def __init__( self, + card_type: str = None, display_name: str = None, id: str = None, mode: str = None, name: str = None, product_code: str = None, quota_type: str = None, - specs: List[ListQuotasResponseBodyQuotasSpecs] = None, + specs: List[ListResourcesResponseBodyResourcesQuotasSpecs] = None, ): + self.card_type = card_type self.display_name = display_name self.id = id self.mode = mode @@ -9945,6 +13659,8 @@ def to_map(self): return _map result = dict() + if self.card_type is not None: + result['CardType'] = self.card_type if self.display_name is not None: result['DisplayName'] = self.display_name if self.id is not None: @@ -9965,6 +13681,8 @@ def to_map(self): def from_map(self, m: dict = None): m = m or dict() + if m.get('CardType') is not None: + self.card_type = m.get('CardType') if m.get('DisplayName') is not None: self.display_name = m.get('DisplayName') if m.get('Id') is not None: @@ -9980,23 +13698,53 @@ def from_map(self, m: dict = None): self.specs = [] if m.get('Specs') is not None: for k in m.get('Specs'): - temp_model = ListQuotasResponseBodyQuotasSpecs() + temp_model = ListResourcesResponseBodyResourcesQuotasSpecs() self.specs.append(temp_model.from_map(k)) return self -class ListQuotasResponseBody(TeaModel): +class ListResourcesResponseBodyResources(TeaModel): def __init__( self, - quotas: List[ListQuotasResponseBodyQuotas] = None, - request_id: str = None, - total_count: int = None, + encryption: ListResourcesResponseBodyResourcesEncryption = None, + env_type: str = None, + executor: ListResourcesResponseBodyResourcesExecutor = None, + gmt_create_time: str = None, + group_name: str = None, + id: str = None, + is_default: bool = None, + labels: List[ListResourcesResponseBodyResourcesLabels] = None, + name: str = None, + product_type: str = None, + quotas: List[ListResourcesResponseBodyResourcesQuotas] = None, + resource_type: str = None, + spec: Dict[str, Any] = None, + workspace_id: str = None, ): + self.encryption = encryption + self.env_type = env_type + self.executor = executor + self.gmt_create_time = gmt_create_time + self.group_name = group_name + self.id = id + self.is_default = is_default + self.labels = labels + self.name = name + self.product_type = product_type self.quotas = quotas - self.request_id = request_id - self.total_count = total_count + self.resource_type = resource_type + self.spec = spec + self.workspace_id = workspace_id def validate(self): + if self.encryption: + self.encryption.validate() + if self.executor: + self.executor.validate() + if self.labels: + for k in self.labels: + if k: + k.validate() if self.quotas: for k in self.quotas: if k: @@ -10008,45 +13756,140 @@ def to_map(self): return _map result = dict() + if self.encryption is not None: + result['Encryption'] = self.encryption.to_map() + if self.env_type is not None: + result['EnvType'] = self.env_type + if self.executor is not None: + result['Executor'] = self.executor.to_map() + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.group_name is not None: + result['GroupName'] = self.group_name + if self.id is not None: + result['Id'] = self.id + if self.is_default is not None: + result['IsDefault'] = self.is_default + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + if self.name is not None: + result['Name'] = self.name + if self.product_type is not None: + result['ProductType'] = self.product_type result['Quotas'] = [] if self.quotas is not None: for k in self.quotas: result['Quotas'].append(k.to_map() if k else None) - if self.request_id is not None: - result['RequestId'] = self.request_id - if self.total_count is not None: - result['TotalCount'] = self.total_count + if self.resource_type is not None: + result['ResourceType'] = self.resource_type + if self.spec is not None: + result['Spec'] = self.spec + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() + if m.get('Encryption') is not None: + temp_model = ListResourcesResponseBodyResourcesEncryption() + self.encryption = temp_model.from_map(m['Encryption']) + if m.get('EnvType') is not None: + self.env_type = m.get('EnvType') + if m.get('Executor') is not None: + temp_model = ListResourcesResponseBodyResourcesExecutor() + self.executor = temp_model.from_map(m['Executor']) + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GroupName') is not None: + self.group_name = m.get('GroupName') + if m.get('Id') is not None: + self.id = m.get('Id') + if m.get('IsDefault') is not None: + self.is_default = m.get('IsDefault') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = ListResourcesResponseBodyResourcesLabels() + self.labels.append(temp_model.from_map(k)) + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('ProductType') is not None: + self.product_type = m.get('ProductType') self.quotas = [] if m.get('Quotas') is not None: for k in m.get('Quotas'): - temp_model = ListQuotasResponseBodyQuotas() + temp_model = ListResourcesResponseBodyResourcesQuotas() self.quotas.append(temp_model.from_map(k)) + if m.get('ResourceType') is not None: + self.resource_type = m.get('ResourceType') + if m.get('Spec') is not None: + self.spec = m.get('Spec') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') + return self + + +class ListResourcesResponseBody(TeaModel): + def __init__( + self, + request_id: str = None, + resources: List[ListResourcesResponseBodyResources] = None, + total_count: int = None, + ): + self.request_id = request_id + self.resources = resources + self.total_count = total_count + + def validate(self): + if self.resources: + for k in self.resources: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.request_id is not None: + result['RequestId'] = self.request_id + result['Resources'] = [] + if self.resources is not None: + for k in self.resources: + result['Resources'].append(k.to_map() if k else None) + if self.total_count is not None: + result['TotalCount'] = self.total_count + return result + + def from_map(self, m: dict = None): + m = m or dict() if m.get('RequestId') is not None: self.request_id = m.get('RequestId') + self.resources = [] + if m.get('Resources') is not None: + for k in m.get('Resources'): + temp_model = ListResourcesResponseBodyResources() + self.resources.append(temp_model.from_map(k)) if m.get('TotalCount') is not None: self.total_count = m.get('TotalCount') return self -class ListQuotasResponse(TeaModel): +class ListResourcesResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListQuotasResponseBody = None, + body: ListResourcesResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -10071,33 +13914,31 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListQuotasResponseBody() + temp_model = ListResourcesResponseBody() self.body = temp_model.from_map(m['body']) return self -class ListResourcesRequest(TeaModel): +class ListServiceTemplatesRequest(TeaModel): def __init__( self, - group_name: str = None, - option: str = None, + label: str = None, + order: str = None, page_number: int = None, page_size: int = None, - product_types: str = None, - resource_name: str = None, - resource_types: str = None, - verbose: bool = None, - workspace_id: str = None, + provider: str = None, + query: str = None, + service_template_name: str = None, + sort_by: str = None, ): - self.group_name = group_name - self.option = option + self.label = label + self.order = order self.page_number = page_number self.page_size = page_size - self.product_types = product_types - self.resource_name = resource_name - self.resource_types = resource_types - self.verbose = verbose - self.workspace_id = workspace_id + self.provider = provider + self.query = query + self.service_template_name = service_template_name + self.sort_by = sort_by def validate(self): pass @@ -10108,62 +13949,61 @@ def to_map(self): return _map result = dict() - if self.group_name is not None: - result['GroupName'] = self.group_name - if self.option is not None: - result['Option'] = self.option + if self.label is not None: + result['Label'] = self.label + if self.order is not None: + result['Order'] = self.order if self.page_number is not None: result['PageNumber'] = self.page_number if self.page_size is not None: result['PageSize'] = self.page_size - if self.product_types is not None: - result['ProductTypes'] = self.product_types - if self.resource_name is not None: - result['ResourceName'] = self.resource_name - if self.resource_types is not None: - result['ResourceTypes'] = self.resource_types - if self.verbose is not None: - result['Verbose'] = self.verbose - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id + if self.provider is not None: + result['Provider'] = self.provider + if self.query is not None: + result['Query'] = self.query + if self.service_template_name is not None: + result['ServiceTemplateName'] = self.service_template_name + if self.sort_by is not None: + result['SortBy'] = self.sort_by return result def from_map(self, m: dict = None): m = m or dict() - if m.get('GroupName') is not None: - self.group_name = m.get('GroupName') - if m.get('Option') is not None: - self.option = m.get('Option') + if m.get('Label') is not None: + self.label = m.get('Label') + if m.get('Order') is not None: + self.order = m.get('Order') if m.get('PageNumber') is not None: self.page_number = m.get('PageNumber') if m.get('PageSize') is not None: self.page_size = m.get('PageSize') - if m.get('ProductTypes') is not None: - self.product_types = m.get('ProductTypes') - if m.get('ResourceName') is not None: - self.resource_name = m.get('ResourceName') - if m.get('ResourceTypes') is not None: - self.resource_types = m.get('ResourceTypes') - if m.get('Verbose') is not None: - self.verbose = m.get('Verbose') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') + if m.get('Provider') is not None: + self.provider = m.get('Provider') + if m.get('Query') is not None: + self.query = m.get('Query') + if m.get('ServiceTemplateName') is not None: + self.service_template_name = m.get('ServiceTemplateName') + if m.get('SortBy') is not None: + self.sort_by = m.get('SortBy') return self -class ListResourcesResponseBodyResourcesEncryption(TeaModel): +class ListServiceTemplatesResponseBody(TeaModel): def __init__( self, - algorithm: str = None, - enabled: bool = None, - key: str = None, + request_id: str = None, + service_templates: List[ServiceTemplate] = None, + total_count: int = None, ): - self.algorithm = algorithm - self.enabled = enabled - self.key = key + self.request_id = request_id + self.service_templates = service_templates + self.total_count = total_count def validate(self): - pass + if self.service_templates: + for k in self.service_templates: + if k: + k.validate() def to_map(self): _map = super().to_map() @@ -10171,36 +14011,44 @@ def to_map(self): return _map result = dict() - if self.algorithm is not None: - result['Algorithm'] = self.algorithm - if self.enabled is not None: - result['Enabled'] = self.enabled - if self.key is not None: - result['Key'] = self.key + if self.request_id is not None: + result['RequestId'] = self.request_id + result['ServiceTemplates'] = [] + if self.service_templates is not None: + for k in self.service_templates: + result['ServiceTemplates'].append(k.to_map() if k else None) + if self.total_count is not None: + result['TotalCount'] = self.total_count return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Algorithm') is not None: - self.algorithm = m.get('Algorithm') - if m.get('Enabled') is not None: - self.enabled = m.get('Enabled') - if m.get('Key') is not None: - self.key = m.get('Key') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + self.service_templates = [] + if m.get('ServiceTemplates') is not None: + for k in m.get('ServiceTemplates'): + temp_model = ServiceTemplate() + self.service_templates.append(temp_model.from_map(k)) + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') return self -class ListResourcesResponseBodyResourcesQuotasSpecs(TeaModel): +class ListServiceTemplatesResponse(TeaModel): def __init__( self, - name: str = None, - value: str = None, + headers: Dict[str, str] = None, + status_code: int = None, + body: ListServiceTemplatesResponseBody = None, ): - self.name = name - self.value = value + self.headers = headers + self.status_code = status_code + self.body = body def validate(self): - pass + if self.body: + self.body.validate() def to_map(self): _map = super().to_map() @@ -10208,47 +14056,37 @@ def to_map(self): return _map result = dict() - if self.name is not None: - result['Name'] = self.name - if self.value is not None: - result['Value'] = self.value + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Name') is not None: - self.name = m.get('Name') - if m.get('Value') is not None: - self.value = m.get('Value') + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = ListServiceTemplatesResponseBody() + self.body = temp_model.from_map(m['body']) return self -class ListResourcesResponseBodyResourcesQuotas(TeaModel): +class ListUserConfigsRequest(TeaModel): def __init__( self, - card_type: str = None, - display_name: str = None, - id: str = None, - mode: str = None, - name: str = None, - product_code: str = None, - quota_type: str = None, - specs: List[ListResourcesResponseBodyResourcesQuotasSpecs] = None, + category_names: str = None, + config_keys: str = None, ): - self.card_type = card_type - self.display_name = display_name - self.id = id - self.mode = mode - self.name = name - self.product_code = product_code - self.quota_type = quota_type - self.specs = specs + self.category_names = category_names + self.config_keys = config_keys def validate(self): - if self.specs: - for k in self.specs: - if k: - k.validate() + pass def to_map(self): _map = super().to_map() @@ -10256,92 +14094,34 @@ def to_map(self): return _map result = dict() - if self.card_type is not None: - result['CardType'] = self.card_type - if self.display_name is not None: - result['DisplayName'] = self.display_name - if self.id is not None: - result['Id'] = self.id - if self.mode is not None: - result['Mode'] = self.mode - if self.name is not None: - result['Name'] = self.name - if self.product_code is not None: - result['ProductCode'] = self.product_code - if self.quota_type is not None: - result['QuotaType'] = self.quota_type - result['Specs'] = [] - if self.specs is not None: - for k in self.specs: - result['Specs'].append(k.to_map() if k else None) + if self.category_names is not None: + result['CategoryNames'] = self.category_names + if self.config_keys is not None: + result['ConfigKeys'] = self.config_keys return result def from_map(self, m: dict = None): m = m or dict() - if m.get('CardType') is not None: - self.card_type = m.get('CardType') - if m.get('DisplayName') is not None: - self.display_name = m.get('DisplayName') - if m.get('Id') is not None: - self.id = m.get('Id') - if m.get('Mode') is not None: - self.mode = m.get('Mode') - if m.get('Name') is not None: - self.name = m.get('Name') - if m.get('ProductCode') is not None: - self.product_code = m.get('ProductCode') - if m.get('QuotaType') is not None: - self.quota_type = m.get('QuotaType') - self.specs = [] - if m.get('Specs') is not None: - for k in m.get('Specs'): - temp_model = ListResourcesResponseBodyResourcesQuotasSpecs() - self.specs.append(temp_model.from_map(k)) + if m.get('CategoryNames') is not None: + self.category_names = m.get('CategoryNames') + if m.get('ConfigKeys') is not None: + self.config_keys = m.get('ConfigKeys') return self -class ListResourcesResponseBodyResources(TeaModel): +class ListUserConfigsResponseBodyConfigs(TeaModel): def __init__( self, - encryption: ListResourcesResponseBodyResourcesEncryption = None, - env_type: str = None, - executor: Dict[str, ResourcesExecutorValue] = None, - gmt_create_time: str = None, - group_name: str = None, - id: str = None, - is_default: bool = None, - name: str = None, - product_type: str = None, - quotas: List[ListResourcesResponseBodyResourcesQuotas] = None, - resource_type: str = None, - spec: Dict[str, Any] = None, - workspace_id: str = None, + category_name: str = None, + config_key: str = None, + config_value: str = None, ): - self.encryption = encryption - self.env_type = env_type - self.executor = executor - self.gmt_create_time = gmt_create_time - self.group_name = group_name - self.id = id - self.is_default = is_default - self.name = name - self.product_type = product_type - self.quotas = quotas - self.resource_type = resource_type - self.spec = spec - self.workspace_id = workspace_id + self.category_name = category_name + self.config_key = config_key + self.config_value = config_value def validate(self): - if self.encryption: - self.encryption.validate() - if self.executor: - for v in self.executor.values(): - if v: - v.validate() - if self.quotas: - for k in self.quotas: - if k: - k.validate() + pass def to_map(self): _map = super().to_map() @@ -10349,90 +14129,39 @@ def to_map(self): return _map result = dict() - if self.encryption is not None: - result['Encryption'] = self.encryption.to_map() - if self.env_type is not None: - result['EnvType'] = self.env_type - result['Executor'] = {} - if self.executor is not None: - for k, v in self.executor.items(): - result['Executor'][k] = v.to_map() - if self.gmt_create_time is not None: - result['GmtCreateTime'] = self.gmt_create_time - if self.group_name is not None: - result['GroupName'] = self.group_name - if self.id is not None: - result['Id'] = self.id - if self.is_default is not None: - result['IsDefault'] = self.is_default - if self.name is not None: - result['Name'] = self.name - if self.product_type is not None: - result['ProductType'] = self.product_type - result['Quotas'] = [] - if self.quotas is not None: - for k in self.quotas: - result['Quotas'].append(k.to_map() if k else None) - if self.resource_type is not None: - result['ResourceType'] = self.resource_type - if self.spec is not None: - result['Spec'] = self.spec - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id + if self.category_name is not None: + result['CategoryName'] = self.category_name + if self.config_key is not None: + result['ConfigKey'] = self.config_key + if self.config_value is not None: + result['ConfigValue'] = self.config_value return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Encryption') is not None: - temp_model = ListResourcesResponseBodyResourcesEncryption() - self.encryption = temp_model.from_map(m['Encryption']) - if m.get('EnvType') is not None: - self.env_type = m.get('EnvType') - self.executor = {} - if m.get('Executor') is not None: - for k, v in m.get('Executor').items(): - temp_model = ResourcesExecutorValue() - self.executor[k] = temp_model.from_map(v) - if m.get('GmtCreateTime') is not None: - self.gmt_create_time = m.get('GmtCreateTime') - if m.get('GroupName') is not None: - self.group_name = m.get('GroupName') - if m.get('Id') is not None: - self.id = m.get('Id') - if m.get('IsDefault') is not None: - self.is_default = m.get('IsDefault') - if m.get('Name') is not None: - self.name = m.get('Name') - if m.get('ProductType') is not None: - self.product_type = m.get('ProductType') - self.quotas = [] - if m.get('Quotas') is not None: - for k in m.get('Quotas'): - temp_model = ListResourcesResponseBodyResourcesQuotas() - self.quotas.append(temp_model.from_map(k)) - if m.get('ResourceType') is not None: - self.resource_type = m.get('ResourceType') - if m.get('Spec') is not None: - self.spec = m.get('Spec') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') + if m.get('CategoryName') is not None: + self.category_name = m.get('CategoryName') + if m.get('ConfigKey') is not None: + self.config_key = m.get('ConfigKey') + if m.get('ConfigValue') is not None: + self.config_value = m.get('ConfigValue') return self -class ListResourcesResponseBody(TeaModel): +class ListUserConfigsResponseBody(TeaModel): def __init__( self, + configs: List[ListUserConfigsResponseBodyConfigs] = None, request_id: str = None, - resources: List[ListResourcesResponseBodyResources] = None, total_count: int = None, ): + self.configs = configs self.request_id = request_id - self.resources = resources self.total_count = total_count def validate(self): - if self.resources: - for k in self.resources: + if self.configs: + for k in self.configs: if k: k.validate() @@ -10442,45 +14171,42 @@ def to_map(self): return _map result = dict() + result['Configs'] = [] + if self.configs is not None: + for k in self.configs: + result['Configs'].append(k.to_map() if k else None) if self.request_id is not None: result['RequestId'] = self.request_id - result['Resources'] = [] - if self.resources is not None: - for k in self.resources: - result['Resources'].append(k.to_map() if k else None) if self.total_count is not None: result['TotalCount'] = self.total_count return result def from_map(self, m: dict = None): m = m or dict() + self.configs = [] + if m.get('Configs') is not None: + for k in m.get('Configs'): + temp_model = ListUserConfigsResponseBodyConfigs() + self.configs.append(temp_model.from_map(k)) if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - self.resources = [] - if m.get('Resources') is not None: - for k in m.get('Resources'): - temp_model = ListResourcesResponseBodyResources() - self.resources.append(temp_model.from_map(k)) if m.get('TotalCount') is not None: self.total_count = m.get('TotalCount') return self -class ListResourcesResponse(TeaModel): +class ListUserConfigsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListResourcesResponseBody = None, + body: ListUserConfigsResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -10505,7 +14231,7 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListResourcesResponseBody() + temp_model = ListUserConfigsResponseBody() self.body = temp_model.from_map(m['body']) return self @@ -10659,9 +14385,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -10691,6 +14414,33 @@ def from_map(self, m: dict = None): return self +class ListWorkspaceUsersRequest(TeaModel): + def __init__( + self, + user_name: str = None, + ): + self.user_name = user_name + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.user_name is not None: + result['UserName'] = self.user_name + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('UserName') is not None: + self.user_name = m.get('UserName') + return self + + class ListWorkspaceUsersResponseBodyUsers(TeaModel): def __init__( self, @@ -10783,9 +14533,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -11054,9 +14801,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -11146,11 +14890,239 @@ def __init__( successful_count: int = None, total_count: int = None, ): - self.failed_count = failed_count - self.migrated_count = migrated_count + self.failed_count = failed_count + self.migrated_count = migrated_count + self.request_id = request_id + self.successful_count = successful_count + self.total_count = total_count + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.failed_count is not None: + result['FailedCount'] = self.failed_count + if self.migrated_count is not None: + result['MigratedCount'] = self.migrated_count + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.successful_count is not None: + result['SuccessfulCount'] = self.successful_count + if self.total_count is not None: + result['TotalCount'] = self.total_count + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('FailedCount') is not None: + self.failed_count = m.get('FailedCount') + if m.get('MigratedCount') is not None: + self.migrated_count = m.get('MigratedCount') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('SuccessfulCount') is not None: + self.successful_count = m.get('SuccessfulCount') + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') + return self + + +class MigrateDatasetsResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: MigrateDatasetsResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = MigrateDatasetsResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class PublishCodeSourceResponseBody(TeaModel): + def __init__( + self, + code_source_id: str = None, + request_id: str = None, + ): + self.code_source_id = code_source_id + self.request_id = request_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.code_source_id is not None: + result['CodeSourceId'] = self.code_source_id + if self.request_id is not None: + result['RequestId'] = self.request_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('CodeSourceId') is not None: + self.code_source_id = m.get('CodeSourceId') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + return self + + +class PublishCodeSourceResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: PublishCodeSourceResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = PublishCodeSourceResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class PublishDatasetResponseBody(TeaModel): + def __init__( + self, + request_id: str = None, + ): + self.request_id = request_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.request_id is not None: + result['RequestId'] = self.request_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + return self + + +class PublishDatasetResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: PublishDatasetResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = PublishDatasetResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class PublishImageResponseBody(TeaModel): + def __init__( + self, + image_id: str = None, + request_id: str = None, + ): + self.image_id = image_id self.request_id = request_id - self.successful_count = successful_count - self.total_count = total_count def validate(self): pass @@ -11161,48 +15133,33 @@ def to_map(self): return _map result = dict() - if self.failed_count is not None: - result['FailedCount'] = self.failed_count - if self.migrated_count is not None: - result['MigratedCount'] = self.migrated_count + if self.image_id is not None: + result['ImageId'] = self.image_id if self.request_id is not None: result['RequestId'] = self.request_id - if self.successful_count is not None: - result['SuccessfulCount'] = self.successful_count - if self.total_count is not None: - result['TotalCount'] = self.total_count return result def from_map(self, m: dict = None): m = m or dict() - if m.get('FailedCount') is not None: - self.failed_count = m.get('FailedCount') - if m.get('MigratedCount') is not None: - self.migrated_count = m.get('MigratedCount') + if m.get('ImageId') is not None: + self.image_id = m.get('ImageId') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('SuccessfulCount') is not None: - self.successful_count = m.get('SuccessfulCount') - if m.get('TotalCount') is not None: - self.total_count = m.get('TotalCount') return self -class MigrateDatasetsResponse(TeaModel): +class PublishImageResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: MigrateDatasetsResponseBody = None, + body: PublishImageResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -11227,18 +15184,16 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = MigrateDatasetsResponseBody() + temp_model = PublishImageResponseBody() self.body = temp_model.from_map(m['body']) return self -class PublishCodeSourceResponseBody(TeaModel): +class RemoveImageResponseBody(TeaModel): def __init__( self, - code_source_id: str = None, request_id: str = None, ): - self.code_source_id = code_source_id self.request_id = request_id def validate(self): @@ -11250,36 +15205,29 @@ def to_map(self): return _map result = dict() - if self.code_source_id is not None: - result['CodeSourceId'] = self.code_source_id if self.request_id is not None: result['RequestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('CodeSourceId') is not None: - self.code_source_id = m.get('CodeSourceId') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') return self -class PublishCodeSourceResponse(TeaModel): +class RemoveImageResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: PublishCodeSourceResponseBody = None, + body: RemoveImageResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -11304,12 +15252,12 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = PublishCodeSourceResponseBody() + temp_model = RemoveImageResponseBody() self.body = temp_model.from_map(m['body']) return self -class PublishDatasetResponseBody(TeaModel): +class RemoveImageLabelsResponseBody(TeaModel): def __init__( self, request_id: str = None, @@ -11336,21 +15284,18 @@ def from_map(self, m: dict = None): return self -class PublishDatasetResponse(TeaModel): +class RemoveImageLabelsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: PublishDatasetResponseBody = None, + body: RemoveImageLabelsResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -11375,18 +15320,16 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = PublishDatasetResponseBody() + temp_model = RemoveImageLabelsResponseBody() self.body = temp_model.from_map(m['body']) return self -class PublishImageResponseBody(TeaModel): +class RemoveMemberRoleResponseBody(TeaModel): def __init__( self, - image_id: str = None, request_id: str = None, ): - self.image_id = image_id self.request_id = request_id def validate(self): @@ -11398,36 +15341,29 @@ def to_map(self): return _map result = dict() - if self.image_id is not None: - result['ImageId'] = self.image_id if self.request_id is not None: result['RequestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ImageId') is not None: - self.image_id = m.get('ImageId') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') return self -class PublishImageResponse(TeaModel): +class RemoveMemberRoleResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: PublishImageResponseBody = None, + body: RemoveMemberRoleResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -11452,12 +15388,12 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = PublishImageResponseBody() + temp_model = RemoveMemberRoleResponseBody() self.body = temp_model.from_map(m['body']) return self -class RemoveImageResponseBody(TeaModel): +class RemoveWorkspaceQuotaResponseBody(TeaModel): def __init__( self, request_id: str = None, @@ -11484,21 +15420,18 @@ def from_map(self, m: dict = None): return self -class RemoveImageResponse(TeaModel): +class RemoveWorkspaceQuotaResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: RemoveImageResponseBody = None, + body: RemoveWorkspaceQuotaResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -11523,12 +15456,47 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = RemoveImageResponseBody() + temp_model = RemoveWorkspaceQuotaResponseBody() self.body = temp_model.from_map(m['body']) return self -class RemoveImageLabelsResponseBody(TeaModel): +class SetExperimentLabelsRequest(TeaModel): + def __init__( + self, + labels: List[LabelInfo] = None, + ): + self.labels = labels + + def validate(self): + if self.labels: + for k in self.labels: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + return result + + def from_map(self, m: dict = None): + m = m or dict() + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = LabelInfo() + self.labels.append(temp_model.from_map(k)) + return self + + +class SetExperimentLabelsResponseBody(TeaModel): def __init__( self, request_id: str = None, @@ -11555,21 +15523,18 @@ def from_map(self, m: dict = None): return self -class RemoveImageLabelsResponse(TeaModel): +class SetExperimentLabelsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: RemoveImageLabelsResponseBody = None, + body: SetExperimentLabelsResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -11594,12 +15559,47 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = RemoveImageLabelsResponseBody() + temp_model = SetExperimentLabelsResponseBody() self.body = temp_model.from_map(m['body']) return self -class RemoveMemberRoleResponseBody(TeaModel): +class SetTrialLabelsRequest(TeaModel): + def __init__( + self, + labels: List[LabelInfo] = None, + ): + self.labels = labels + + def validate(self): + if self.labels: + for k in self.labels: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + return result + + def from_map(self, m: dict = None): + m = m or dict() + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = LabelInfo() + self.labels.append(temp_model.from_map(k)) + return self + + +class SetTrialLabelsResponseBody(TeaModel): def __init__( self, request_id: str = None, @@ -11626,21 +15626,18 @@ def from_map(self, m: dict = None): return self -class RemoveMemberRoleResponse(TeaModel): +class SetTrialLabelsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: RemoveMemberRoleResponseBody = None, + body: SetTrialLabelsResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -11665,12 +15662,86 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = RemoveMemberRoleResponseBody() + temp_model = SetTrialLabelsResponseBody() self.body = temp_model.from_map(m['body']) return self -class RemoveWorkspaceQuotaResponseBody(TeaModel): +class SetUserConfigsRequestConfigs(TeaModel): + def __init__( + self, + category_name: str = None, + config_key: str = None, + config_value: str = None, + ): + self.category_name = category_name + self.config_key = config_key + self.config_value = config_value + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.category_name is not None: + result['CategoryName'] = self.category_name + if self.config_key is not None: + result['ConfigKey'] = self.config_key + if self.config_value is not None: + result['ConfigValue'] = self.config_value + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('CategoryName') is not None: + self.category_name = m.get('CategoryName') + if m.get('ConfigKey') is not None: + self.config_key = m.get('ConfigKey') + if m.get('ConfigValue') is not None: + self.config_value = m.get('ConfigValue') + return self + + +class SetUserConfigsRequest(TeaModel): + def __init__( + self, + configs: List[SetUserConfigsRequestConfigs] = None, + ): + self.configs = configs + + def validate(self): + if self.configs: + for k in self.configs: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + result['Configs'] = [] + if self.configs is not None: + for k in self.configs: + result['Configs'].append(k.to_map() if k else None) + return result + + def from_map(self, m: dict = None): + m = m or dict() + self.configs = [] + if m.get('Configs') is not None: + for k in m.get('Configs'): + temp_model = SetUserConfigsRequestConfigs() + self.configs.append(temp_model.from_map(k)) + return self + + +class SetUserConfigsResponseBody(TeaModel): def __init__( self, request_id: str = None, @@ -11697,21 +15768,18 @@ def from_map(self, m: dict = None): return self -class RemoveWorkspaceQuotaResponse(TeaModel): +class SetUserConfigsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: RemoveWorkspaceQuotaResponseBody = None, + body: SetUserConfigsResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -11736,7 +15804,7 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = RemoveWorkspaceQuotaResponseBody() + temp_model = SetUserConfigsResponseBody() self.body = temp_model.from_map(m['body']) return self @@ -11780,9 +15848,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -11812,17 +15877,55 @@ def from_map(self, m: dict = None): return self +class UpdateConfigsRequestConfigsLabels(TeaModel): + def __init__( + self, + key: str = None, + value: str = None, + ): + self.key = key + self.value = value + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.key is not None: + result['Key'] = self.key + if self.value is not None: + result['Value'] = self.value + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Key') is not None: + self.key = m.get('Key') + if m.get('Value') is not None: + self.value = m.get('Value') + return self + + class UpdateConfigsRequestConfigs(TeaModel): def __init__( self, config_key: str = None, config_value: str = None, + labels: List[UpdateConfigsRequestConfigsLabels] = None, ): self.config_key = config_key self.config_value = config_value + self.labels = labels def validate(self): - pass + if self.labels: + for k in self.labels: + if k: + k.validate() def to_map(self): _map = super().to_map() @@ -11834,6 +15937,10 @@ def to_map(self): result['ConfigKey'] = self.config_key if self.config_value is not None: result['ConfigValue'] = self.config_value + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) return result def from_map(self, m: dict = None): @@ -11842,6 +15949,11 @@ def from_map(self, m: dict = None): self.config_key = m.get('ConfigKey') if m.get('ConfigValue') is not None: self.config_value = m.get('ConfigValue') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = UpdateConfigsRequestConfigsLabels() + self.labels.append(temp_model.from_map(k)) return self @@ -11919,9 +16031,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -12029,9 +16138,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -12127,9 +16233,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -12159,22 +16262,130 @@ def from_map(self, m: dict = None): return self +class UpdateExperimentRequest(TeaModel): + def __init__( + self, + accessibility: str = None, + name: str = None, + ): + self.accessibility = accessibility + # 名称 + self.name = name + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.accessibility is not None: + result['Accessibility'] = self.accessibility + if self.name is not None: + result['Name'] = self.name + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Accessibility') is not None: + self.accessibility = m.get('Accessibility') + if m.get('Name') is not None: + self.name = m.get('Name') + return self + + +class UpdateExperimentResponseBody(TeaModel): + def __init__( + self, + request_id: str = None, + ): + self.request_id = request_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.request_id is not None: + result['RequestId'] = self.request_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + return self + + +class UpdateExperimentResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: UpdateExperimentResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = UpdateExperimentResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + class UpdateModelRequest(TeaModel): def __init__( self, accessibility: str = None, domain: str = None, + extra_info: Dict[str, Any] = None, model_description: str = None, model_doc: str = None, model_name: str = None, + model_type: str = None, + order_number: int = None, origin: str = None, task: str = None, ): self.accessibility = accessibility self.domain = domain + self.extra_info = extra_info self.model_description = model_description self.model_doc = model_doc self.model_name = model_name + self.model_type = model_type + self.order_number = order_number self.origin = origin self.task = task @@ -12191,12 +16402,18 @@ def to_map(self): result['Accessibility'] = self.accessibility if self.domain is not None: result['Domain'] = self.domain + if self.extra_info is not None: + result['ExtraInfo'] = self.extra_info if self.model_description is not None: result['ModelDescription'] = self.model_description if self.model_doc is not None: result['ModelDoc'] = self.model_doc if self.model_name is not None: result['ModelName'] = self.model_name + if self.model_type is not None: + result['ModelType'] = self.model_type + if self.order_number is not None: + result['OrderNumber'] = self.order_number if self.origin is not None: result['Origin'] = self.origin if self.task is not None: @@ -12209,12 +16426,18 @@ def from_map(self, m: dict = None): self.accessibility = m.get('Accessibility') if m.get('Domain') is not None: self.domain = m.get('Domain') + if m.get('ExtraInfo') is not None: + self.extra_info = m.get('ExtraInfo') if m.get('ModelDescription') is not None: self.model_description = m.get('ModelDescription') if m.get('ModelDoc') is not None: self.model_doc = m.get('ModelDoc') if m.get('ModelName') is not None: self.model_name = m.get('ModelName') + if m.get('ModelType') is not None: + self.model_type = m.get('ModelType') + if m.get('OrderNumber') is not None: + self.order_number = m.get('OrderNumber') if m.get('Origin') is not None: self.origin = m.get('Origin') if m.get('Task') is not None: @@ -12261,9 +16484,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -12471,9 +16691,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -12507,6 +16724,8 @@ class UpdateModelVersionRequest(TeaModel): def __init__( self, approval_status: str = None, + evaluation_spec: Dict[str, Any] = None, + extra_info: Dict[str, Any] = None, inference_spec: Dict[str, Any] = None, metrics: Dict[str, Any] = None, options: str = None, @@ -12516,6 +16735,8 @@ def __init__( version_description: str = None, ): self.approval_status = approval_status + self.evaluation_spec = evaluation_spec + self.extra_info = extra_info self.inference_spec = inference_spec self.metrics = metrics self.options = options @@ -12535,6 +16756,10 @@ def to_map(self): result = dict() if self.approval_status is not None: result['ApprovalStatus'] = self.approval_status + if self.evaluation_spec is not None: + result['EvaluationSpec'] = self.evaluation_spec + if self.extra_info is not None: + result['ExtraInfo'] = self.extra_info if self.inference_spec is not None: result['InferenceSpec'] = self.inference_spec if self.metrics is not None: @@ -12555,6 +16780,10 @@ def from_map(self, m: dict = None): m = m or dict() if m.get('ApprovalStatus') is not None: self.approval_status = m.get('ApprovalStatus') + if m.get('EvaluationSpec') is not None: + self.evaluation_spec = m.get('EvaluationSpec') + if m.get('ExtraInfo') is not None: + self.extra_info = m.get('ExtraInfo') if m.get('InferenceSpec') is not None: self.inference_spec = m.get('InferenceSpec') if m.get('Metrics') is not None: @@ -12611,9 +16840,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -12643,6 +16869,125 @@ def from_map(self, m: dict = None): return self +class UpdateServiceTemplateRequest(TeaModel): + def __init__( + self, + inference_spec: Dict[str, Any] = None, + order_number: int = None, + service_template_description: str = None, + service_template_doc: str = None, + service_template_name: str = None, + ): + self.inference_spec = inference_spec + self.order_number = order_number + self.service_template_description = service_template_description + self.service_template_doc = service_template_doc + self.service_template_name = service_template_name + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.inference_spec is not None: + result['InferenceSpec'] = self.inference_spec + if self.order_number is not None: + result['OrderNumber'] = self.order_number + if self.service_template_description is not None: + result['ServiceTemplateDescription'] = self.service_template_description + if self.service_template_doc is not None: + result['ServiceTemplateDoc'] = self.service_template_doc + if self.service_template_name is not None: + result['ServiceTemplateName'] = self.service_template_name + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('InferenceSpec') is not None: + self.inference_spec = m.get('InferenceSpec') + if m.get('OrderNumber') is not None: + self.order_number = m.get('OrderNumber') + if m.get('ServiceTemplateDescription') is not None: + self.service_template_description = m.get('ServiceTemplateDescription') + if m.get('ServiceTemplateDoc') is not None: + self.service_template_doc = m.get('ServiceTemplateDoc') + if m.get('ServiceTemplateName') is not None: + self.service_template_name = m.get('ServiceTemplateName') + return self + + +class UpdateServiceTemplateResponseBody(TeaModel): + def __init__( + self, + request_id: str = None, + ): + self.request_id = request_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.request_id is not None: + result['RequestId'] = self.request_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + return self + + +class UpdateServiceTemplateResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: UpdateServiceTemplateResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = UpdateServiceTemplateResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + class UpdateWorkspaceRequest(TeaModel): def __init__( self, @@ -12715,9 +17060,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -12747,21 +17089,63 @@ def from_map(self, m: dict = None): return self +class UpdateWorkspaceResourceRequestLabels(TeaModel): + def __init__( + self, + key: str = None, + value: str = None, + ): + self.key = key + self.value = value + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.key is not None: + result['Key'] = self.key + if self.value is not None: + result['Value'] = self.value + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Key') is not None: + self.key = m.get('Key') + if m.get('Value') is not None: + self.value = m.get('Value') + return self + + class UpdateWorkspaceResourceRequest(TeaModel): def __init__( self, group_name: str = None, is_default: bool = None, + labels: List[UpdateWorkspaceResourceRequestLabels] = None, product_type: str = None, + resource_ids: List[str] = None, resource_type: str = None, + spec: Dict[str, Any] = None, ): self.group_name = group_name self.is_default = is_default + self.labels = labels self.product_type = product_type + self.resource_ids = resource_ids self.resource_type = resource_type + self.spec = spec def validate(self): - pass + if self.labels: + for k in self.labels: + if k: + k.validate() def to_map(self): _map = super().to_map() @@ -12773,10 +17157,18 @@ def to_map(self): result['GroupName'] = self.group_name if self.is_default is not None: result['IsDefault'] = self.is_default + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) if self.product_type is not None: result['ProductType'] = self.product_type + if self.resource_ids is not None: + result['ResourceIds'] = self.resource_ids if self.resource_type is not None: result['ResourceType'] = self.resource_type + if self.spec is not None: + result['Spec'] = self.spec return result def from_map(self, m: dict = None): @@ -12785,10 +17177,19 @@ def from_map(self, m: dict = None): self.group_name = m.get('GroupName') if m.get('IsDefault') is not None: self.is_default = m.get('IsDefault') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = UpdateWorkspaceResourceRequestLabels() + self.labels.append(temp_model.from_map(k)) if m.get('ProductType') is not None: self.product_type = m.get('ProductType') + if m.get('ResourceIds') is not None: + self.resource_ids = m.get('ResourceIds') if m.get('ResourceType') is not None: self.resource_type = m.get('ResourceType') + if m.get('Spec') is not None: + self.spec = m.get('Spec') return self @@ -12796,8 +17197,10 @@ class UpdateWorkspaceResourceResponseBody(TeaModel): def __init__( self, request_id: str = None, + resource_ids: List[str] = None, ): self.request_id = request_id + self.resource_ids = resource_ids def validate(self): pass @@ -12810,12 +17213,16 @@ def to_map(self): result = dict() if self.request_id is not None: result['RequestId'] = self.request_id + if self.resource_ids is not None: + result['ResourceIds'] = self.resource_ids return result def from_map(self, m: dict = None): m = m or dict() if m.get('RequestId') is not None: self.request_id = m.get('RequestId') + if m.get('ResourceIds') is not None: + self.resource_ids = m.get('ResourceIds') return self @@ -12831,9 +17238,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() From f82074d5e0d57822f694f7d934016122d091b575 Mon Sep 17 00:00:00 2001 From: yangziming Date: Tue, 20 Feb 2024 10:09:12 +0800 Subject: [PATCH 14/59] fix: fix outdated unit tests --- tests/integration/test_model.py | 2 +- tests/integration/test_modelscope.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration/test_model.py b/tests/integration/test_model.py index 568803b..4305fef 100644 --- a/tests/integration/test_model.py +++ b/tests/integration/test_model.py @@ -254,7 +254,7 @@ def test_tmp_algo_rm_train(self): self.assertTrue(outputs_data) self.assertTrue(len(outputs_data) == 1) - model_path = os.path.join(outputs_data["model"], "pytorch_model.bin") + model_path = os.path.join(outputs_data["model"], "model.safetensors") self.assertTrue(self.is_oss_object_exists(model_path)) @pytest.mark.timeout(60 * 10) diff --git a/tests/integration/test_modelscope.py b/tests/integration/test_modelscope.py index fa357b7..95766df 100644 --- a/tests/integration/test_modelscope.py +++ b/tests/integration/test_modelscope.py @@ -30,7 +30,7 @@ def test_base(self): est = ModelScopeEstimator( command="python -c 'import modelscope; print(modelscope.__version__)'", instance_type="ecs.c6.large", - modelscope_version="1.6.1", + modelscope_version="1.12.0", base_job_name="sdk-ms-train", ) self.assertIsNotNone(est.training_image_uri()) From 7161b1058bfb9156ac8bf2bc167939628276dec2 Mon Sep 17 00:00:00 2001 From: yangziming Date: Wed, 27 Mar 2024 19:06:16 +0800 Subject: [PATCH 15/59] feat: add processor user guide --- docs/source/api/estimator.rst | 2 +- docs/source/api/processor.rst | 6 + docs/source/index.rst | 1 + docs/source/reference.rst | 1 + docs/source/user-guide/processing-job.rst | 214 ++++++++++++++++++++++ 5 files changed, 223 insertions(+), 1 deletion(-) create mode 100644 docs/source/api/processor.rst create mode 100644 docs/source/user-guide/processing-job.rst diff --git a/docs/source/api/estimator.rst b/docs/source/api/estimator.rst index a87f7d1..ff70670 100644 --- a/docs/source/api/estimator.rst +++ b/docs/source/api/estimator.rst @@ -9,7 +9,7 @@ Estimator :members: :show-inheritance: -.. autoclass:: pai.estimator.UserVpcConfig +.. autoclass:: pai.common.configs.UserVpcConfig :members: :show-inheritance: diff --git a/docs/source/api/processor.rst b/docs/source/api/processor.rst new file mode 100644 index 0000000..64d7747 --- /dev/null +++ b/docs/source/api/processor.rst @@ -0,0 +1,6 @@ +Processor +--------- + +.. autoclass:: pai.processor.Processor + :members: + :show-inheritance: diff --git a/docs/source/index.rst b/docs/source/index.rst index 154a6a6..f426f83 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -27,6 +27,7 @@ PAI Python SDK 文档 user-guide/train user-guide/inference user-guide/pretrained-model + user-guide/processing-job .. toctree:: diff --git a/docs/source/reference.rst b/docs/source/reference.rst index b4cb15f..0bdd211 100644 --- a/docs/source/reference.rst +++ b/docs/source/reference.rst @@ -5,6 +5,7 @@ API 文档 :maxdepth: 1 api/estimator + api/processor api/model api/image api/predictor diff --git a/docs/source/user-guide/processing-job.rst b/docs/source/user-guide/processing-job.rst new file mode 100644 index 0000000..1c61024 --- /dev/null +++ b/docs/source/user-guide/processing-job.rst @@ -0,0 +1,214 @@ +===================== +通用作业(Experimental) +===================== + +SDK提供了HighLevel的通用任务API: :class:`~pai.processor.Processor` 支持用户提交通用作业到PAI,使用示例如下。 + +.. code-block:: python + + from pai.processor import Processor + + # 通过 Processor 配置通用作业的信息 + processor = Processor( + command="" + source_dir="" + image_uri="" + instance_type="", + parameters={ + "interval": 500, + "max_retry": 5, + }, + ) + + # 指定作业的输入和输出,并提交作业 + processor.run( + inputs={ + "inputs": "oss:///path/to/input/", + }, + outputs={ + "outputs": "oss:///path/to/output/", + }, + ) + +用户可以通过提交通用作业来完成自定义训练、数据处理等一系列任务,本文档将介绍如何通过 Processor 来提交通用作业。 + +.. note:: 通用作业为实验性功能,在未来版本中可能会变更或者移除。 + +准备作业脚本 +***************** + +通过 :class:`~pai.processor.Processor` 的 ``source_dir`` 参数, +开发者可以配置需要上传执行的代码目录。在通用作业提交之后, +相应的代码会被上传到用户OSS,并在作业启动之前被下载到作业的执行环境中。 + +用户代码目录示例: + +.. code-block:: shell + + |-- code_dir # 用户指定上传的代码目录 + `-- main.py # 作业脚本,用户可以通过 python main.py 的命令拉起脚本 + `-- utils.py + + +通过 ``Processor`` 设置使用的代码目录,可以是绝对路径或是相对路径。 + +.. code-block:: python + + processor = Processor( + command="python main.py", + # 可以通过相对路径或是绝对路径的方式指定代码. + source_dir="code_dir/", + # source_dir="/home/foo/code_dir/", + ) + +作业代码会被下载到作业执行环境的 ``/ml/usercode`` 目录下,作业启动前会切换 working directory 至 ``/ml/usercode`` 目录。 + +.. code-block:: shell + + |-- /ml/usercode/ # 作业代码所在目录 + `-- main.py + `-- utils.py + +用户也可以通过传递一个OSS Bucket路径,作为作业代码路径。 + +.. code-block:: python + + from pai.common.oss_utils import upload + + # 上传代码到OSS,返回一个OSS URI + code_uri = upload( + local_path="./code_dir/", + oss_path="path/for/code/" + ) + # code_uri: oss:///path/for/code/ + + processor = Processor( + command="python main.py", + # 使用OSS上的作业代码 + source_dir=code_uri, + ) + + +配置作业镜像 +***************** + +在提交执行作业时,用户需要配置作业运行使用的镜像 ( :class:`~pai.processor.Processor` 的 ``image_uri`` +参数),镜像内包含作业执行所需的依赖,例如Python、CUDA、机器学习框架、以及依赖的第三方库等,从而支持代码运行。 + +用户可以配置使用阿里云镜像仓库内的镜像,也可以使用PAI提供的公共镜像(推荐)。对于常见的机器学习框架,PAI提供了公共镜像供用户使用,用户可以通过以下的代码获取镜像信息: + +.. note:: + + 用户可以通过PAI `公共镜像文档 `_ 查看PAI提供的镜像内安装的Python三方库信息。 + +.. note:: + + 企业版容器镜像服务ACR默认需要通过用户的VPC访问镜像仓库,具体请参考文档: `配置专有网络的访问控制 `_。 + 作业的机器实例位于云产品PAI的VPC环境内,需要通过配置 :class:`~pai.processor.Processor` 的 ``user_vpc_config`` 参数,将作业实例与用户VPC网络进行连接,作业才能通过用户VPC访问到企业版镜像仓库,拉取镜像。 + +.. code-block:: python + + from pai.image import retrieve, list_images + + # 获取PAI提供的最新的PyTorch的GPU镜像 + # 通过参数 framework_version="latest",retrieve 方法会返回最新的 PyTorch 镜像 + print(retrieve(framework_name="TensorFlow", framework_version="latest", + accelerator_type="GPU")) + + # 获取PAI提供的所有PyTorch镜像 + for image_info in list_images(framework_name="PyTorch"): + print(image_info) + + +安装代码依赖 +************************************************ + +当代码有额外的Python包依赖,可以通过在代码目录下编写 `requirements.txt `_ ,相应的三方库依赖会在用户脚本执行前被安装到作业环境中。 + +配置使用 ``requirements.txt`` 的作业代码目录示例如下: + +.. code-block:: shell + + |-- code_dir # 作业配置使用的脚本目录 + |-- requirements.txt # 作业的requirements信息 + `-- main.py + `-- utils.py + + +执行作业 +***************** + +用户通过构建 :class:`~pai.processor.Processor` 指定作业的脚本目录、启动脚本、参数、机器资源等, +然后通过 :meth:`~pai.processor.Processor.run` 方法提交作业。在提交作业之后,SDK会打印作业的控制台URL, +并持续打印作业的输出日志信息,直到作业结束退出(作业状态为成功,失败,或是被停止)。 + +用户可以通过作业URL,去控制台查看作业执行详情、日志、机器的资源使用情况、以及作业的Metrics等信息。 +在作业执行完成之后退出,可以通过 :meth:`~pai.processor.Processor.get_outputs_data` 方法获得提交作业的产出的模型的OSS路径。 + +示例代码如下: + +.. code-block:: python + + from pai.processor import Processor + from pai.image import retrieve + + # 获取PAI支持的最新 PyTorch 镜像 + image_uri = retrieve("PyTorch", accelerator_type="GPU").image_uri + + processor = Processor( + # 作业的启动命令 + command="python main.py", + # 作业脚本所在目录 + source_dir="./code_dir/", + # 作业使用的镜像 + image_uri=_image_uri, + # 作业使用的机器类型, 支持的机器类型见文档 https://help.aliyun.com/document_detail/171758.html#section-55y-4tq-84y + instance_type="ecs.c6.xlarge", + # 作业的参数 + parameters={ + "interval": 500, + "max_retry": 5, + }, + # 作业名称前缀,用户提交的作业使用的Name为 `{base_job_name}_{submitted-datetime}` + base_job_name="example_processing_job", + ) + + # 提交作业,同时打印作业的Web详情页URL。 + # run 方法默认等待到作业终止(成功,失败,会是被停止)。 + processor.run( + inputs={ + "inputs": "oss:///path/to/input/", + }, + outputs={ + "outputs": "oss:///path/to/output/", + }, + ) + + +下载作业输出 +***************** + +作业执行完成之后,用户可以通过 :meth:`pai.processor.Processor.get_outputs_data` +获得提交作业输出的OSS路径。用户可以通过SDK提供的 +``download`` 方法下载模型到本地,也可以使用 ``ossutil`` 命令行工具下载模型。 + + +使用 ``pai.common.oss_utils.download`` 方法下载模型到本地: + +.. code-block:: python + + from pai.common.oss_utils import download + + outputs = processor.get_outputs_data() + + # 下载模型到本地 + download(oss_path=outputs["outputs"], local_path="./outputs/") + + +通过 ``ossutil`` 命令行工具下载模型到本地。 + +.. code-block:: shell + + ossutil cp -r ./outputs/ + +对于 ``ossutil`` 命令行工具的使用,可以参考 `ossutil工具使用文档 `_ 。 From c0716790c3fe0e7f446be017803a40939d2095a2 Mon Sep 17 00:00:00 2001 From: yangziming Date: Tue, 9 Apr 2024 15:07:10 +0800 Subject: [PATCH 16/59] feat: add list datasets utils and processor waiting all job done --- pai/api/dataset.py | 5 ++++ pai/common/consts.py | 1 - pai/common/utils.py | 66 +++++++++++++++++++++++++++++++++++++++++++- pai/dataset.py | 41 +++++++++++++++++++++++++++ pai/estimator.py | 13 +++++++-- pai/processor.py | 63 ++++++++++++++++++++++++++++++++++++------ 6 files changed, 176 insertions(+), 13 deletions(-) create mode 100644 pai/dataset.py diff --git a/pai/api/dataset.py b/pai/api/dataset.py index f693e3e..5cdfe06 100644 --- a/pai/api/dataset.py +++ b/pai/api/dataset.py @@ -46,6 +46,8 @@ def list( name: str = None, page_size: int = 20, page_number: int = 1, + order: str = "DESC", + **kwargs, ) -> PaginatedResult: """Returns Dataset in paging. @@ -54,6 +56,7 @@ def list( name: Name of the Dataset. page_number: Page number. page_size: Page size. + order: Return list order. Returns: int: A list of datasets match the conditions. @@ -64,6 +67,8 @@ def list( name=name, page_size=page_size, page_number=page_number, + order=order, + **kwargs, ) resp: ListDatasetsResponseBody = self._do_request( diff --git a/pai/common/consts.py b/pai/common/consts.py index 2cfd3a1..b4dc8d9 100644 --- a/pai/common/consts.py +++ b/pai/common/consts.py @@ -97,7 +97,6 @@ class FrameworkTypes(object): class FileSystemInputScheme(object): - # Standard/Extreme/CPFS 1.0 file system type NAS = "nas" # CPFS2.0 file system type diff --git a/pai/common/utils.py b/pai/common/utils.py index ec543d3..e6dddb0 100644 --- a/pai/common/utils.py +++ b/pai/common/utils.py @@ -14,13 +14,16 @@ from __future__ import absolute_import +import functools import random import re import socket import string import sys +import time +import warnings from functools import lru_cache -from typing import Callable, Dict, Optional, Union +from typing import Callable, Dict, List, Optional, Union from semantic_version import Version @@ -252,3 +255,64 @@ def is_domain_connectable(domain: str, port: int = 80, timeout: int = 1) -> bool finally: # Close the socket sock.close() + + +def experimental(callable_entity): + """Decorator to mark functions or classes as experimental""" + + @functools.wraps(callable_entity) + def wrapper(*args, **kwargs): + message = f"{callable_entity.__name__} is experimental and may change or be removed in future releases." + warnings.warn(message, category=FutureWarning, stacklevel=2) + return callable_entity(*args, **kwargs) + + return wrapper + + +def retry(max_attempts=3, wait_secs=1, exceptions=(Exception,), report_retries=True): + """Decorator to make functions retry by config""" + + def decorator_retry(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + attempts = 0 + while attempts < max_attempts: + try: + result = func(*args, **kwargs) + return result + except exceptions as e: + attempts += 1 + if attempts == max_attempts: + raise # Re-raise the last exception when all the attempts have failed + if report_retries: + warnings.warn(f"Retry {attempts}/{max_attempts} failed: {e}") + time.sleep(wait_secs) + + return wrapper + + return decorator_retry + + +def print_table(headers: List[str], rows: List[List[str]]): + """Give headers and rows, print as table to stdout.""" + + length = len(headers) + for row in rows: + if len(row) != length: + raise ValueError("Unable to print table, headers length mismatch with rows") + + column_widths = [ + max(len(str(value)) for value in column) for column in zip(headers, *rows) + ] + header_row = " | ".join( + f"{header:<{column_widths[i]}}" for i, header in enumerate(headers) + ) + + print(header_row) + print("-" * len(header_row)) + for row in rows: + print( + " | ".join( + f"{str(value):<{column_widths[i]}}" for i, value in enumerate(row) + ) + ) diff --git a/pai/dataset.py b/pai/dataset.py new file mode 100644 index 0000000..1c33427 --- /dev/null +++ b/pai/dataset.py @@ -0,0 +1,41 @@ +# Copyright 2024 Alibaba, Inc. or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging +from typing import Any, Dict, List, Optional + +from .common import ProviderAlibabaPAI +from .common.utils import make_list_resource_iterator +from .session import Session, get_default_session + +logger = logging.getLogger(__name__) + + +def list_common_datasets( + name: str = None, + session: Optional[Session] = None, +) -> List[Dict[str, Any]]: + session = session or get_default_session() + + gen = make_list_resource_iterator( + session.dataset_api.list, + name=name, + provider=ProviderAlibabaPAI, + # set the workspace_id manually, prevent using the default workspace of the + # session. + workspace_id=0, + order="DESC", + ) + + return gen diff --git a/pai/estimator.py b/pai/estimator.py index 0952600..27806c1 100644 --- a/pai/estimator.py +++ b/pai/estimator.py @@ -44,6 +44,7 @@ is_odps_table_uri, make_list_resource_iterator, random_str, + retry, to_plain_text, ) from .exception import UnexpectedStatusException @@ -1778,9 +1779,8 @@ def wait(self, interval=2, show_logs: bool = True): else: job_log_printer = None try: - while self.status not in TrainingJobStatus.completed_status(): + while not self.is_completed(): time.sleep(interval) - self.session.training_job_api.refresh_entity(self.training_job_id, self) finally: if job_log_printer: job_log_printer.stop(wait=True) @@ -1823,6 +1823,15 @@ def is_succeeded(self): self._reload() return self.status == TrainingJobStatus.Succeed + @retry(wait_secs=10) + def is_completed(self): + """Return True if the training job is completed, including failed status""" + if self.status in TrainingJobStatus.completed_status(): + return True + self._reload() + + return self.status in TrainingJobStatus.completed_status() + class _TrainingJobLogPrinter(object): """A class used to print logs for a training job""" diff --git a/pai/processor.py b/pai/processor.py index d831f8e..0b2f23a 100644 --- a/pai/processor.py +++ b/pai/processor.py @@ -14,6 +14,7 @@ import logging import os import posixpath +import time from datetime import datetime from typing import Any, Dict, List, Optional, Union @@ -21,6 +22,7 @@ from .common.consts import DefaultChannelName, JobType, StoragePathCategory from .common.oss_utils import OssUriObj, is_oss_uri, upload from .common.utils import ( + experimental, is_dataset_id, is_filesystem_uri, is_odps_table_uri, @@ -131,6 +133,7 @@ def as_oss_dir_uri(uri: str): return config +@experimental class Processor(object): def __init__( self, @@ -309,6 +312,7 @@ def run( job = self._fit(inputs=inputs, outputs=outputs, job_name=job_name) self._latest_job = job + self._jobs.append(job) if wait: self.wait(show_logs=show_logs) @@ -363,28 +367,69 @@ def _fit( input_channels=input_configs, output_channels=output_configs, algorithm_spec=algo_spec, - user_vpc_config=self.user_vpc_config.to_dict() - if self.user_vpc_config - else None, + user_vpc_config=self.ut() if self.user_vpc_config else None, ) job = _Job.get(job_id) - print(f"View the job detail by accessing the console URI: {job.console_uri}") + print(f"View the job {job_id} by accessing the console URI: {job.console_uri}") return job - def wait(self, show_logs: bool = True): - """Block until the latest job is completed. + def wait(self, interval: int = 2, show_logs: bool = True, all_jobs: bool = False): + """Block until the jobs is completed. Args: + interval(int): Interval to reload job status show_logs(bool): Specifies whether to fetch and print the logs produced by the job. + all_jobs(bool): Wait latest job or wait all jobs in processor, show_logs disabled while + wait all jobs. Raises: RuntimeError: If no job is submitted. """ - if not self._latest_job: - raise RuntimeError("Could not find a submitted job.") - self._latest_job.wait(show_logs=show_logs) + if all_jobs: + if not self._jobs: + raise RuntimeError("Could not find any submitted job.") + + remains = set(self._jobs) + while remains: + for job in self._jobs: + if job in remains and job.is_completed(): + remains.remove(job) + + time.sleep(interval) + + self._generate_jobs_report() + else: + if not self._latest_job: + raise RuntimeError("Could not find a submitted job.") + + self._latest_job.wait(interval=interval, show_logs=show_logs) + + def _generate_jobs_report(self): + """Generate current jobs report and output to stdout""" + print(f"Jobs status report, total jobs count: {len(self._jobs)}") + + rows = [] + headers = ["JobName", "JobID", "Status"] + for job in self._jobs: + rows.append([job.training_job_name, job.id, job.status]) + + column_widths = [ + max(len(str(value)) for value in column) for column in zip(headers, *rows) + ] + header_row = " | ".join( + f"{header:<{column_widths[i]}}" for i, header in enumerate(headers) + ) + + print(header_row) + print("-" * len(header_row)) + for row in rows: + print( + " | ".join( + f"{str(value):<{column_widths[i]}}" for i, value in enumerate(row) + ) + ) def _get_job_base_output_path(self, job_name: str) -> str: """Generate the base output path for the job.""" From a63b18150c9f32292052de6bda262571e1479e33 Mon Sep 17 00:00:00 2001 From: yangziming Date: Fri, 22 Mar 2024 10:50:36 +0800 Subject: [PATCH 17/59] chore: bump paistudio client to v1.1.7 --- .../__init__.py | 2 +- .../alibabacloud_paistudio20220112/client.py | 3170 ++- .../alibabacloud_paistudio20220112/models.py | 17084 +++++++++++----- 3 files changed, 14554 insertions(+), 5702 deletions(-) diff --git a/pai/libs/alibabacloud_paistudio20220112/__init__.py b/pai/libs/alibabacloud_paistudio20220112/__init__.py index d706e92..d294ca8 100644 --- a/pai/libs/alibabacloud_paistudio20220112/__init__.py +++ b/pai/libs/alibabacloud_paistudio20220112/__init__.py @@ -1 +1 @@ -__version__ = '1.0.13' \ No newline at end of file +__version__ = '1.1.7' \ No newline at end of file diff --git a/pai/libs/alibabacloud_paistudio20220112/client.py b/pai/libs/alibabacloud_paistudio20220112/client.py index 5427831..0b61fc4 100644 --- a/pai/libs/alibabacloud_paistudio20220112/client.py +++ b/pai/libs/alibabacloud_paistudio20220112/client.py @@ -7,19 +7,17 @@ from alibabacloud_tea_openapi import models as open_api_models from alibabacloud_tea_util.client import Client as UtilClient from alibabacloud_endpoint_util.client import Client as EndpointUtilClient -# from alibabacloud_paistudio20220112 import models as pai_studio_20220112_models +from pai.libs.alibabacloud_paistudio20220112 import models as pai_studio_20220112_models from alibabacloud_tea_util import models as util_models from alibabacloud_openapi_util.client import Client as OpenApiUtilClient -from pai.libs.alibabacloud_paistudio20220112 import models as pai_studio_20220112_models - class Client(OpenApiClient): """ *\ """ def __init__( - self, + self, config: open_api_models.Config, ): super().__init__(config) @@ -61,6 +59,186 @@ def get_endpoint( return endpoint_map.get(region_id) return EndpointUtilClient.get_endpoint_rules(product_id, region_id, endpoint_rule, network, suffix) + def build_llmsnapshot_with_options( + self, + project_id: str, + snapshot_id: str, + request: pai_studio_20220112_models.BuildLLMSnapshotRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.BuildLLMSnapshotResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.description): + body['Description'] = request.description + if not UtilClient.is_unset(request.display_name): + body['DisplayName'] = request.display_name + if not UtilClient.is_unset(request.labels): + body['Labels'] = request.labels + if not UtilClient.is_unset(request.workload): + body['Workload'] = request.workload + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='BuildLLMSnapshot', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/langstudio/projects/{OpenApiUtilClient.get_encode_param(project_id)}/snapshots/{OpenApiUtilClient.get_encode_param(snapshot_id)}/build', + method='PUT', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.BuildLLMSnapshotResponse(), + self.call_api(params, req, runtime) + ) + + async def build_llmsnapshot_with_options_async( + self, + project_id: str, + snapshot_id: str, + request: pai_studio_20220112_models.BuildLLMSnapshotRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.BuildLLMSnapshotResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.description): + body['Description'] = request.description + if not UtilClient.is_unset(request.display_name): + body['DisplayName'] = request.display_name + if not UtilClient.is_unset(request.labels): + body['Labels'] = request.labels + if not UtilClient.is_unset(request.workload): + body['Workload'] = request.workload + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='BuildLLMSnapshot', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/langstudio/projects/{OpenApiUtilClient.get_encode_param(project_id)}/snapshots/{OpenApiUtilClient.get_encode_param(snapshot_id)}/build', + method='PUT', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.BuildLLMSnapshotResponse(), + await self.call_api_async(params, req, runtime) + ) + + def build_llmsnapshot( + self, + project_id: str, + snapshot_id: str, + request: pai_studio_20220112_models.BuildLLMSnapshotRequest, + ) -> pai_studio_20220112_models.BuildLLMSnapshotResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.build_llmsnapshot_with_options(project_id, snapshot_id, request, headers, runtime) + + async def build_llmsnapshot_async( + self, + project_id: str, + snapshot_id: str, + request: pai_studio_20220112_models.BuildLLMSnapshotRequest, + ) -> pai_studio_20220112_models.BuildLLMSnapshotResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.build_llmsnapshot_with_options_async(project_id, snapshot_id, request, headers, runtime) + + def check_instance_web_terminal_with_options( + self, + training_job_id: str, + instance_id: str, + request: pai_studio_20220112_models.CheckInstanceWebTerminalRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.CheckInstanceWebTerminalResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.check_info): + body['CheckInfo'] = request.check_info + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='CheckInstanceWebTerminal', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/trainingjobs/{OpenApiUtilClient.get_encode_param(training_job_id)}/instances/{OpenApiUtilClient.get_encode_param(instance_id)}/webterminals/action/check', + method='POST', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.CheckInstanceWebTerminalResponse(), + self.call_api(params, req, runtime) + ) + + async def check_instance_web_terminal_with_options_async( + self, + training_job_id: str, + instance_id: str, + request: pai_studio_20220112_models.CheckInstanceWebTerminalRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.CheckInstanceWebTerminalResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.check_info): + body['CheckInfo'] = request.check_info + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='CheckInstanceWebTerminal', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/trainingjobs/{OpenApiUtilClient.get_encode_param(training_job_id)}/instances/{OpenApiUtilClient.get_encode_param(instance_id)}/webterminals/action/check', + method='POST', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.CheckInstanceWebTerminalResponse(), + await self.call_api_async(params, req, runtime) + ) + + def check_instance_web_terminal( + self, + training_job_id: str, + instance_id: str, + request: pai_studio_20220112_models.CheckInstanceWebTerminalRequest, + ) -> pai_studio_20220112_models.CheckInstanceWebTerminalResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.check_instance_web_terminal_with_options(training_job_id, instance_id, request, headers, runtime) + + async def check_instance_web_terminal_async( + self, + training_job_id: str, + instance_id: str, + request: pai_studio_20220112_models.CheckInstanceWebTerminalRequest, + ) -> pai_studio_20220112_models.CheckInstanceWebTerminalResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.check_instance_web_terminal_with_options_async(training_job_id, instance_id, request, headers, runtime) + def create_ai4ddefault_bucket_with_options( self, headers: Dict[str, str], @@ -571,39 +749,21 @@ async def create_component_version_async( headers = {} return await self.create_component_version_with_options_async(component_id, request, headers, runtime) - def create_quota_with_options( + def create_instance_web_terminal_with_options( self, - request: pai_studio_20220112_models.CreateQuotaRequest, + training_job_id: str, + instance_id: str, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.CreateQuotaResponse: - UtilClient.validate_model(request) - body = {} - if not UtilClient.is_unset(request.allocate_strategy): - body['AllocateStrategy'] = request.allocate_strategy - if not UtilClient.is_unset(request.description): - body['Description'] = request.description - if not UtilClient.is_unset(request.labels): - body['Labels'] = request.labels - if not UtilClient.is_unset(request.min): - body['Min'] = request.min - if not UtilClient.is_unset(request.parent_quota_id): - body['ParentQuotaId'] = request.parent_quota_id - if not UtilClient.is_unset(request.quota_name): - body['QuotaName'] = request.quota_name - if not UtilClient.is_unset(request.resource_group_ids): - body['ResourceGroupIds'] = request.resource_group_ids - if not UtilClient.is_unset(request.resource_type): - body['ResourceType'] = request.resource_type + ) -> pai_studio_20220112_models.CreateInstanceWebTerminalResponse: req = open_api_models.OpenApiRequest( - headers=headers, - body=OpenApiUtilClient.parse_to_map(body) + headers=headers ) params = open_api_models.Params( - action='CreateQuota', + action='CreateInstanceWebTerminal', version='2022-01-12', protocol='HTTPS', - pathname=f'/api/v1/quotas', + pathname=f'/api/v1/trainingjobs/{OpenApiUtilClient.get_encode_param(training_job_id)}/instances/{OpenApiUtilClient.get_encode_param(instance_id)}/webterminals', method='POST', auth_type='AK', style='ROA', @@ -611,43 +771,25 @@ def create_quota_with_options( body_type='json' ) return TeaCore.from_map( - pai_studio_20220112_models.CreateQuotaResponse(), + pai_studio_20220112_models.CreateInstanceWebTerminalResponse(), self.call_api(params, req, runtime) ) - async def create_quota_with_options_async( + async def create_instance_web_terminal_with_options_async( self, - request: pai_studio_20220112_models.CreateQuotaRequest, + training_job_id: str, + instance_id: str, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.CreateQuotaResponse: - UtilClient.validate_model(request) - body = {} - if not UtilClient.is_unset(request.allocate_strategy): - body['AllocateStrategy'] = request.allocate_strategy - if not UtilClient.is_unset(request.description): - body['Description'] = request.description - if not UtilClient.is_unset(request.labels): - body['Labels'] = request.labels - if not UtilClient.is_unset(request.min): - body['Min'] = request.min - if not UtilClient.is_unset(request.parent_quota_id): - body['ParentQuotaId'] = request.parent_quota_id - if not UtilClient.is_unset(request.quota_name): - body['QuotaName'] = request.quota_name - if not UtilClient.is_unset(request.resource_group_ids): - body['ResourceGroupIds'] = request.resource_group_ids - if not UtilClient.is_unset(request.resource_type): - body['ResourceType'] = request.resource_type + ) -> pai_studio_20220112_models.CreateInstanceWebTerminalResponse: req = open_api_models.OpenApiRequest( - headers=headers, - body=OpenApiUtilClient.parse_to_map(body) + headers=headers ) params = open_api_models.Params( - action='CreateQuota', + action='CreateInstanceWebTerminal', version='2022-01-12', protocol='HTTPS', - pathname=f'/api/v1/quotas', + pathname=f'/api/v1/trainingjobs/{OpenApiUtilClient.get_encode_param(training_job_id)}/instances/{OpenApiUtilClient.get_encode_param(instance_id)}/webterminals', method='POST', auth_type='AK', style='ROA', @@ -655,53 +797,59 @@ async def create_quota_with_options_async( body_type='json' ) return TeaCore.from_map( - pai_studio_20220112_models.CreateQuotaResponse(), + pai_studio_20220112_models.CreateInstanceWebTerminalResponse(), await self.call_api_async(params, req, runtime) ) - def create_quota( + def create_instance_web_terminal( self, - request: pai_studio_20220112_models.CreateQuotaRequest, - ) -> pai_studio_20220112_models.CreateQuotaResponse: + training_job_id: str, + instance_id: str, + ) -> pai_studio_20220112_models.CreateInstanceWebTerminalResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.create_quota_with_options(request, headers, runtime) + return self.create_instance_web_terminal_with_options(training_job_id, instance_id, headers, runtime) - async def create_quota_async( + async def create_instance_web_terminal_async( self, - request: pai_studio_20220112_models.CreateQuotaRequest, - ) -> pai_studio_20220112_models.CreateQuotaResponse: + training_job_id: str, + instance_id: str, + ) -> pai_studio_20220112_models.CreateInstanceWebTerminalResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.create_quota_with_options_async(request, headers, runtime) + return await self.create_instance_web_terminal_with_options_async(training_job_id, instance_id, headers, runtime) - def create_resource_group_with_options( + def create_llmproject_with_options( self, - request: pai_studio_20220112_models.CreateResourceGroupRequest, + request: pai_studio_20220112_models.CreateLLMProjectRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.CreateResourceGroupResponse: + ) -> pai_studio_20220112_models.CreateLLMProjectResponse: UtilClient.validate_model(request) body = {} - if not UtilClient.is_unset(request.computing_resource_provider): - body['ComputingResourceProvider'] = request.computing_resource_provider - if not UtilClient.is_unset(request.description): - body['Description'] = request.description - if not UtilClient.is_unset(request.name): - body['Name'] = request.name - if not UtilClient.is_unset(request.resource_type): - body['ResourceType'] = request.resource_type - if not UtilClient.is_unset(request.user_vpc): - body['UserVpc'] = request.user_vpc + if not UtilClient.is_unset(request.labels): + body['Labels'] = request.labels + if not UtilClient.is_unset(request.project_description): + body['ProjectDescription'] = request.project_description + if not UtilClient.is_unset(request.project_name): + body['ProjectName'] = request.project_name + if not UtilClient.is_unset(request.project_type): + body['ProjectType'] = request.project_type + if not UtilClient.is_unset(request.root_path): + body['RootPath'] = request.root_path + if not UtilClient.is_unset(request.runtime): + body['Runtime'] = request.runtime + if not UtilClient.is_unset(request.workspace_id): + body['WorkspaceId'] = request.workspace_id req = open_api_models.OpenApiRequest( headers=headers, body=OpenApiUtilClient.parse_to_map(body) ) params = open_api_models.Params( - action='CreateResourceGroup', + action='CreateLLMProject', version='2022-01-12', protocol='HTTPS', - pathname=f'/api/v1/resources', + pathname=f'/api/v1/langstudio/projects', method='POST', auth_type='AK', style='ROA', @@ -709,37 +857,41 @@ def create_resource_group_with_options( body_type='json' ) return TeaCore.from_map( - pai_studio_20220112_models.CreateResourceGroupResponse(), + pai_studio_20220112_models.CreateLLMProjectResponse(), self.call_api(params, req, runtime) ) - async def create_resource_group_with_options_async( + async def create_llmproject_with_options_async( self, - request: pai_studio_20220112_models.CreateResourceGroupRequest, + request: pai_studio_20220112_models.CreateLLMProjectRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.CreateResourceGroupResponse: + ) -> pai_studio_20220112_models.CreateLLMProjectResponse: UtilClient.validate_model(request) body = {} - if not UtilClient.is_unset(request.computing_resource_provider): - body['ComputingResourceProvider'] = request.computing_resource_provider - if not UtilClient.is_unset(request.description): - body['Description'] = request.description - if not UtilClient.is_unset(request.name): - body['Name'] = request.name - if not UtilClient.is_unset(request.resource_type): - body['ResourceType'] = request.resource_type - if not UtilClient.is_unset(request.user_vpc): - body['UserVpc'] = request.user_vpc + if not UtilClient.is_unset(request.labels): + body['Labels'] = request.labels + if not UtilClient.is_unset(request.project_description): + body['ProjectDescription'] = request.project_description + if not UtilClient.is_unset(request.project_name): + body['ProjectName'] = request.project_name + if not UtilClient.is_unset(request.project_type): + body['ProjectType'] = request.project_type + if not UtilClient.is_unset(request.root_path): + body['RootPath'] = request.root_path + if not UtilClient.is_unset(request.runtime): + body['Runtime'] = request.runtime + if not UtilClient.is_unset(request.workspace_id): + body['WorkspaceId'] = request.workspace_id req = open_api_models.OpenApiRequest( headers=headers, body=OpenApiUtilClient.parse_to_map(body) ) params = open_api_models.Params( - action='CreateResourceGroup', + action='CreateLLMProject', version='2022-01-12', protocol='HTTPS', - pathname=f'/api/v1/resources', + pathname=f'/api/v1/langstudio/projects', method='POST', auth_type='AK', style='ROA', @@ -747,32 +899,32 @@ async def create_resource_group_with_options_async( body_type='json' ) return TeaCore.from_map( - pai_studio_20220112_models.CreateResourceGroupResponse(), + pai_studio_20220112_models.CreateLLMProjectResponse(), await self.call_api_async(params, req, runtime) ) - def create_resource_group( + def create_llmproject( self, - request: pai_studio_20220112_models.CreateResourceGroupRequest, - ) -> pai_studio_20220112_models.CreateResourceGroupResponse: + request: pai_studio_20220112_models.CreateLLMProjectRequest, + ) -> pai_studio_20220112_models.CreateLLMProjectResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.create_resource_group_with_options(request, headers, runtime) + return self.create_llmproject_with_options(request, headers, runtime) - async def create_resource_group_async( + async def create_llmproject_async( self, - request: pai_studio_20220112_models.CreateResourceGroupRequest, - ) -> pai_studio_20220112_models.CreateResourceGroupResponse: + request: pai_studio_20220112_models.CreateLLMProjectRequest, + ) -> pai_studio_20220112_models.CreateLLMProjectResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.create_resource_group_with_options_async(request, headers, runtime) + return await self.create_llmproject_with_options_async(request, headers, runtime) - def create_service_identity_role_with_options( + def create_llmservice_identity_role_with_options( self, - request: pai_studio_20220112_models.CreateServiceIdentityRoleRequest, + request: pai_studio_20220112_models.CreateLLMServiceIdentityRoleRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.CreateServiceIdentityRoleResponse: + ) -> pai_studio_20220112_models.CreateLLMServiceIdentityRoleResponse: UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.role_name): @@ -782,10 +934,10 @@ def create_service_identity_role_with_options( body=OpenApiUtilClient.parse_to_map(body) ) params = open_api_models.Params( - action='CreateServiceIdentityRole', + action='CreateLLMServiceIdentityRole', version='2022-01-12', protocol='HTTPS', - pathname=f'/api/v1/ai4d/serviceidentityroles', + pathname=f'/api/v1/langstudio/serviceidentityroles', method='POST', auth_type='AK', style='ROA', @@ -793,7 +945,475 @@ def create_service_identity_role_with_options( body_type='json' ) return TeaCore.from_map( - pai_studio_20220112_models.CreateServiceIdentityRoleResponse(), + pai_studio_20220112_models.CreateLLMServiceIdentityRoleResponse(), + self.call_api(params, req, runtime) + ) + + async def create_llmservice_identity_role_with_options_async( + self, + request: pai_studio_20220112_models.CreateLLMServiceIdentityRoleRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.CreateLLMServiceIdentityRoleResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.role_name): + body['RoleName'] = request.role_name + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='CreateLLMServiceIdentityRole', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/langstudio/serviceidentityroles', + method='POST', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.CreateLLMServiceIdentityRoleResponse(), + await self.call_api_async(params, req, runtime) + ) + + def create_llmservice_identity_role( + self, + request: pai_studio_20220112_models.CreateLLMServiceIdentityRoleRequest, + ) -> pai_studio_20220112_models.CreateLLMServiceIdentityRoleResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.create_llmservice_identity_role_with_options(request, headers, runtime) + + async def create_llmservice_identity_role_async( + self, + request: pai_studio_20220112_models.CreateLLMServiceIdentityRoleRequest, + ) -> pai_studio_20220112_models.CreateLLMServiceIdentityRoleResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.create_llmservice_identity_role_with_options_async(request, headers, runtime) + + def create_llmsnapshot_with_options( + self, + project_id: str, + request: pai_studio_20220112_models.CreateLLMSnapshotRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.CreateLLMSnapshotResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.storage): + body['Storage'] = request.storage + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='CreateLLMSnapshot', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/langstudio/projects/{OpenApiUtilClient.get_encode_param(project_id)}/snapshots', + method='POST', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.CreateLLMSnapshotResponse(), + self.call_api(params, req, runtime) + ) + + async def create_llmsnapshot_with_options_async( + self, + project_id: str, + request: pai_studio_20220112_models.CreateLLMSnapshotRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.CreateLLMSnapshotResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.storage): + body['Storage'] = request.storage + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='CreateLLMSnapshot', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/langstudio/projects/{OpenApiUtilClient.get_encode_param(project_id)}/snapshots', + method='POST', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.CreateLLMSnapshotResponse(), + await self.call_api_async(params, req, runtime) + ) + + def create_llmsnapshot( + self, + project_id: str, + request: pai_studio_20220112_models.CreateLLMSnapshotRequest, + ) -> pai_studio_20220112_models.CreateLLMSnapshotResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.create_llmsnapshot_with_options(project_id, request, headers, runtime) + + async def create_llmsnapshot_async( + self, + project_id: str, + request: pai_studio_20220112_models.CreateLLMSnapshotRequest, + ) -> pai_studio_20220112_models.CreateLLMSnapshotResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.create_llmsnapshot_with_options_async(project_id, request, headers, runtime) + + def create_quota_with_options( + self, + request: pai_studio_20220112_models.CreateQuotaRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.CreateQuotaResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.allocate_strategy): + body['AllocateStrategy'] = request.allocate_strategy + if not UtilClient.is_unset(request.description): + body['Description'] = request.description + if not UtilClient.is_unset(request.labels): + body['Labels'] = request.labels + if not UtilClient.is_unset(request.min): + body['Min'] = request.min + if not UtilClient.is_unset(request.parent_quota_id): + body['ParentQuotaId'] = request.parent_quota_id + if not UtilClient.is_unset(request.queue_strategy): + body['QueueStrategy'] = request.queue_strategy + if not UtilClient.is_unset(request.quota_config): + body['QuotaConfig'] = request.quota_config + if not UtilClient.is_unset(request.quota_name): + body['QuotaName'] = request.quota_name + if not UtilClient.is_unset(request.resource_group_ids): + body['ResourceGroupIds'] = request.resource_group_ids + if not UtilClient.is_unset(request.resource_type): + body['ResourceType'] = request.resource_type + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='CreateQuota', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/quotas', + method='POST', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.CreateQuotaResponse(), + self.call_api(params, req, runtime) + ) + + async def create_quota_with_options_async( + self, + request: pai_studio_20220112_models.CreateQuotaRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.CreateQuotaResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.allocate_strategy): + body['AllocateStrategy'] = request.allocate_strategy + if not UtilClient.is_unset(request.description): + body['Description'] = request.description + if not UtilClient.is_unset(request.labels): + body['Labels'] = request.labels + if not UtilClient.is_unset(request.min): + body['Min'] = request.min + if not UtilClient.is_unset(request.parent_quota_id): + body['ParentQuotaId'] = request.parent_quota_id + if not UtilClient.is_unset(request.queue_strategy): + body['QueueStrategy'] = request.queue_strategy + if not UtilClient.is_unset(request.quota_config): + body['QuotaConfig'] = request.quota_config + if not UtilClient.is_unset(request.quota_name): + body['QuotaName'] = request.quota_name + if not UtilClient.is_unset(request.resource_group_ids): + body['ResourceGroupIds'] = request.resource_group_ids + if not UtilClient.is_unset(request.resource_type): + body['ResourceType'] = request.resource_type + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='CreateQuota', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/quotas', + method='POST', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.CreateQuotaResponse(), + await self.call_api_async(params, req, runtime) + ) + + def create_quota( + self, + request: pai_studio_20220112_models.CreateQuotaRequest, + ) -> pai_studio_20220112_models.CreateQuotaResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.create_quota_with_options(request, headers, runtime) + + async def create_quota_async( + self, + request: pai_studio_20220112_models.CreateQuotaRequest, + ) -> pai_studio_20220112_models.CreateQuotaResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.create_quota_with_options_async(request, headers, runtime) + + def create_resource_group_with_options( + self, + request: pai_studio_20220112_models.CreateResourceGroupRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.CreateResourceGroupResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.computing_resource_provider): + body['ComputingResourceProvider'] = request.computing_resource_provider + if not UtilClient.is_unset(request.description): + body['Description'] = request.description + if not UtilClient.is_unset(request.name): + body['Name'] = request.name + if not UtilClient.is_unset(request.resource_type): + body['ResourceType'] = request.resource_type + if not UtilClient.is_unset(request.tag): + body['Tag'] = request.tag + if not UtilClient.is_unset(request.user_vpc): + body['UserVpc'] = request.user_vpc + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='CreateResourceGroup', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/resources', + method='POST', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.CreateResourceGroupResponse(), + self.call_api(params, req, runtime) + ) + + async def create_resource_group_with_options_async( + self, + request: pai_studio_20220112_models.CreateResourceGroupRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.CreateResourceGroupResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.computing_resource_provider): + body['ComputingResourceProvider'] = request.computing_resource_provider + if not UtilClient.is_unset(request.description): + body['Description'] = request.description + if not UtilClient.is_unset(request.name): + body['Name'] = request.name + if not UtilClient.is_unset(request.resource_type): + body['ResourceType'] = request.resource_type + if not UtilClient.is_unset(request.tag): + body['Tag'] = request.tag + if not UtilClient.is_unset(request.user_vpc): + body['UserVpc'] = request.user_vpc + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='CreateResourceGroup', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/resources', + method='POST', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.CreateResourceGroupResponse(), + await self.call_api_async(params, req, runtime) + ) + + def create_resource_group( + self, + request: pai_studio_20220112_models.CreateResourceGroupRequest, + ) -> pai_studio_20220112_models.CreateResourceGroupResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.create_resource_group_with_options(request, headers, runtime) + + async def create_resource_group_async( + self, + request: pai_studio_20220112_models.CreateResourceGroupRequest, + ) -> pai_studio_20220112_models.CreateResourceGroupResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.create_resource_group_with_options_async(request, headers, runtime) + + def create_resource_group_machine_group_with_options( + self, + resource_group_id: str, + request: pai_studio_20220112_models.CreateResourceGroupMachineGroupRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.CreateResourceGroupMachineGroupResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.ecs_count): + body['EcsCount'] = request.ecs_count + if not UtilClient.is_unset(request.ecs_spec): + body['EcsSpec'] = request.ecs_spec + if not UtilClient.is_unset(request.name): + body['Name'] = request.name + if not UtilClient.is_unset(request.payment_duration): + body['PaymentDuration'] = request.payment_duration + if not UtilClient.is_unset(request.payment_duration_unit): + body['PaymentDurationUnit'] = request.payment_duration_unit + if not UtilClient.is_unset(request.payment_type): + body['PaymentType'] = request.payment_type + if not UtilClient.is_unset(request.tag): + body['Tag'] = request.tag + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='CreateResourceGroupMachineGroup', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/resources/{OpenApiUtilClient.get_encode_param(resource_group_id)}/machinegroups', + method='POST', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.CreateResourceGroupMachineGroupResponse(), + self.call_api(params, req, runtime) + ) + + async def create_resource_group_machine_group_with_options_async( + self, + resource_group_id: str, + request: pai_studio_20220112_models.CreateResourceGroupMachineGroupRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.CreateResourceGroupMachineGroupResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.ecs_count): + body['EcsCount'] = request.ecs_count + if not UtilClient.is_unset(request.ecs_spec): + body['EcsSpec'] = request.ecs_spec + if not UtilClient.is_unset(request.name): + body['Name'] = request.name + if not UtilClient.is_unset(request.payment_duration): + body['PaymentDuration'] = request.payment_duration + if not UtilClient.is_unset(request.payment_duration_unit): + body['PaymentDurationUnit'] = request.payment_duration_unit + if not UtilClient.is_unset(request.payment_type): + body['PaymentType'] = request.payment_type + if not UtilClient.is_unset(request.tag): + body['Tag'] = request.tag + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='CreateResourceGroupMachineGroup', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/resources/{OpenApiUtilClient.get_encode_param(resource_group_id)}/machinegroups', + method='POST', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.CreateResourceGroupMachineGroupResponse(), + await self.call_api_async(params, req, runtime) + ) + + def create_resource_group_machine_group( + self, + resource_group_id: str, + request: pai_studio_20220112_models.CreateResourceGroupMachineGroupRequest, + ) -> pai_studio_20220112_models.CreateResourceGroupMachineGroupResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.create_resource_group_machine_group_with_options(resource_group_id, request, headers, runtime) + + async def create_resource_group_machine_group_async( + self, + resource_group_id: str, + request: pai_studio_20220112_models.CreateResourceGroupMachineGroupRequest, + ) -> pai_studio_20220112_models.CreateResourceGroupMachineGroupResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.create_resource_group_machine_group_with_options_async(resource_group_id, request, headers, runtime) + + def create_service_identity_role_with_options( + self, + request: pai_studio_20220112_models.CreateServiceIdentityRoleRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.CreateServiceIdentityRoleResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.role_name): + body['RoleName'] = request.role_name + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='CreateServiceIdentityRole', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/ai4d/serviceidentityroles', + method='POST', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.CreateServiceIdentityRoleResponse(), self.call_api(params, req, runtime) ) @@ -863,6 +1483,10 @@ def create_training_job_with_options( body['CodeDir'] = request.code_dir if not UtilClient.is_unset(request.compute_resource): body['ComputeResource'] = request.compute_resource + if not UtilClient.is_unset(request.environments): + body['Environments'] = request.environments + if not UtilClient.is_unset(request.experiment_config): + body['ExperimentConfig'] = request.experiment_config if not UtilClient.is_unset(request.hyper_parameters): body['HyperParameters'] = request.hyper_parameters if not UtilClient.is_unset(request.input_channels): @@ -871,10 +1495,14 @@ def create_training_job_with_options( body['Labels'] = request.labels if not UtilClient.is_unset(request.output_channels): body['OutputChannels'] = request.output_channels + if not UtilClient.is_unset(request.python_requirements): + body['PythonRequirements'] = request.python_requirements if not UtilClient.is_unset(request.role_arn): body['RoleArn'] = request.role_arn if not UtilClient.is_unset(request.scheduler): body['Scheduler'] = request.scheduler + if not UtilClient.is_unset(request.settings): + body['Settings'] = request.settings if not UtilClient.is_unset(request.training_job_description): body['TrainingJobDescription'] = request.training_job_description if not UtilClient.is_unset(request.training_job_name): @@ -923,6 +1551,10 @@ async def create_training_job_with_options_async( body['CodeDir'] = request.code_dir if not UtilClient.is_unset(request.compute_resource): body['ComputeResource'] = request.compute_resource + if not UtilClient.is_unset(request.environments): + body['Environments'] = request.environments + if not UtilClient.is_unset(request.experiment_config): + body['ExperimentConfig'] = request.experiment_config if not UtilClient.is_unset(request.hyper_parameters): body['HyperParameters'] = request.hyper_parameters if not UtilClient.is_unset(request.input_channels): @@ -931,10 +1563,14 @@ async def create_training_job_with_options_async( body['Labels'] = request.labels if not UtilClient.is_unset(request.output_channels): body['OutputChannels'] = request.output_channels + if not UtilClient.is_unset(request.python_requirements): + body['PythonRequirements'] = request.python_requirements if not UtilClient.is_unset(request.role_arn): body['RoleArn'] = request.role_arn if not UtilClient.is_unset(request.scheduler): body['Scheduler'] = request.scheduler + if not UtilClient.is_unset(request.settings): + body['Settings'] = request.settings if not UtilClient.is_unset(request.training_job_description): body['TrainingJobDescription'] = request.training_job_description if not UtilClient.is_unset(request.training_job_name): @@ -1317,6 +1953,72 @@ async def delete_component_version_snapshot_async( headers = {} return await self.delete_component_version_snapshot_with_options_async(snapshot_id, headers, runtime) + def delete_llmproject_with_options( + self, + project_id: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.DeleteLLMProjectResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='DeleteLLMProject', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/langstudio/projects/{OpenApiUtilClient.get_encode_param(project_id)}', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.DeleteLLMProjectResponse(), + self.call_api(params, req, runtime) + ) + + async def delete_llmproject_with_options_async( + self, + project_id: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.DeleteLLMProjectResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='DeleteLLMProject', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/langstudio/projects/{OpenApiUtilClient.get_encode_param(project_id)}', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.DeleteLLMProjectResponse(), + await self.call_api_async(params, req, runtime) + ) + + def delete_llmproject( + self, + project_id: str, + ) -> pai_studio_20220112_models.DeleteLLMProjectResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.delete_llmproject_with_options(project_id, headers, runtime) + + async def delete_llmproject_async( + self, + project_id: str, + ) -> pai_studio_20220112_models.DeleteLLMProjectResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.delete_llmproject_with_options_async(project_id, headers, runtime) + def delete_machine_group_with_options( self, machine_group_id: str, @@ -1726,90 +2428,186 @@ def delete_training_job( async def delete_training_job_async( self, training_job_id: str, - ) -> pai_studio_20220112_models.DeleteTrainingJobResponse: + ) -> pai_studio_20220112_models.DeleteTrainingJobResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.delete_training_job_with_options_async(training_job_id, headers, runtime) + + def delete_training_job_labels_with_options( + self, + training_job_id: str, + request: pai_studio_20220112_models.DeleteTrainingJobLabelsRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.DeleteTrainingJobLabelsResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.keys): + query['Keys'] = request.keys + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='DeleteTrainingJobLabels', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/trainingjobs/{OpenApiUtilClient.get_encode_param(training_job_id)}/labels', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.DeleteTrainingJobLabelsResponse(), + self.call_api(params, req, runtime) + ) + + async def delete_training_job_labels_with_options_async( + self, + training_job_id: str, + request: pai_studio_20220112_models.DeleteTrainingJobLabelsRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.DeleteTrainingJobLabelsResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.keys): + query['Keys'] = request.keys + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='DeleteTrainingJobLabels', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/trainingjobs/{OpenApiUtilClient.get_encode_param(training_job_id)}/labels', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.DeleteTrainingJobLabelsResponse(), + await self.call_api_async(params, req, runtime) + ) + + def delete_training_job_labels( + self, + training_job_id: str, + request: pai_studio_20220112_models.DeleteTrainingJobLabelsRequest, + ) -> pai_studio_20220112_models.DeleteTrainingJobLabelsResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.delete_training_job_labels_with_options(training_job_id, request, headers, runtime) + + async def delete_training_job_labels_async( + self, + training_job_id: str, + request: pai_studio_20220112_models.DeleteTrainingJobLabelsRequest, + ) -> pai_studio_20220112_models.DeleteTrainingJobLabelsResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.delete_training_job_with_options_async(training_job_id, headers, runtime) + return await self.delete_training_job_labels_with_options_async(training_job_id, request, headers, runtime) - def delete_training_job_labels_with_options( + def deploy_llmsnapshot_with_options( self, - training_job_id: str, - request: pai_studio_20220112_models.DeleteTrainingJobLabelsRequest, + project_id: str, + snapshot_id: str, + request: pai_studio_20220112_models.DeployLLMSnapshotRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.DeleteTrainingJobLabelsResponse: + ) -> pai_studio_20220112_models.DeployLLMSnapshotResponse: UtilClient.validate_model(request) - query = {} - if not UtilClient.is_unset(request.keys): - query['Keys'] = request.keys + body = {} + if not UtilClient.is_unset(request.description): + body['Description'] = request.description + if not UtilClient.is_unset(request.display_name): + body['DisplayName'] = request.display_name + if not UtilClient.is_unset(request.labels): + body['Labels'] = request.labels + if not UtilClient.is_unset(request.workload): + body['Workload'] = request.workload req = open_api_models.OpenApiRequest( headers=headers, - query=OpenApiUtilClient.query(query) + body=OpenApiUtilClient.parse_to_map(body) ) params = open_api_models.Params( - action='DeleteTrainingJobLabels', + action='DeployLLMSnapshot', version='2022-01-12', protocol='HTTPS', - pathname=f'/api/v1/trainingjobs/{OpenApiUtilClient.get_encode_param(training_job_id)}/labels', - method='DELETE', + pathname=f'/api/v1/langstudio/projects/{OpenApiUtilClient.get_encode_param(project_id)}/snapshots/{OpenApiUtilClient.get_encode_param(snapshot_id)}/deploy', + method='PUT', auth_type='AK', style='ROA', req_body_type='json', body_type='json' ) return TeaCore.from_map( - pai_studio_20220112_models.DeleteTrainingJobLabelsResponse(), + pai_studio_20220112_models.DeployLLMSnapshotResponse(), self.call_api(params, req, runtime) ) - async def delete_training_job_labels_with_options_async( + async def deploy_llmsnapshot_with_options_async( self, - training_job_id: str, - request: pai_studio_20220112_models.DeleteTrainingJobLabelsRequest, + project_id: str, + snapshot_id: str, + request: pai_studio_20220112_models.DeployLLMSnapshotRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.DeleteTrainingJobLabelsResponse: + ) -> pai_studio_20220112_models.DeployLLMSnapshotResponse: UtilClient.validate_model(request) - query = {} - if not UtilClient.is_unset(request.keys): - query['Keys'] = request.keys + body = {} + if not UtilClient.is_unset(request.description): + body['Description'] = request.description + if not UtilClient.is_unset(request.display_name): + body['DisplayName'] = request.display_name + if not UtilClient.is_unset(request.labels): + body['Labels'] = request.labels + if not UtilClient.is_unset(request.workload): + body['Workload'] = request.workload req = open_api_models.OpenApiRequest( headers=headers, - query=OpenApiUtilClient.query(query) + body=OpenApiUtilClient.parse_to_map(body) ) params = open_api_models.Params( - action='DeleteTrainingJobLabels', + action='DeployLLMSnapshot', version='2022-01-12', protocol='HTTPS', - pathname=f'/api/v1/trainingjobs/{OpenApiUtilClient.get_encode_param(training_job_id)}/labels', - method='DELETE', + pathname=f'/api/v1/langstudio/projects/{OpenApiUtilClient.get_encode_param(project_id)}/snapshots/{OpenApiUtilClient.get_encode_param(snapshot_id)}/deploy', + method='PUT', auth_type='AK', style='ROA', req_body_type='json', body_type='json' ) return TeaCore.from_map( - pai_studio_20220112_models.DeleteTrainingJobLabelsResponse(), + pai_studio_20220112_models.DeployLLMSnapshotResponse(), await self.call_api_async(params, req, runtime) ) - def delete_training_job_labels( + def deploy_llmsnapshot( self, - training_job_id: str, - request: pai_studio_20220112_models.DeleteTrainingJobLabelsRequest, - ) -> pai_studio_20220112_models.DeleteTrainingJobLabelsResponse: + project_id: str, + snapshot_id: str, + request: pai_studio_20220112_models.DeployLLMSnapshotRequest, + ) -> pai_studio_20220112_models.DeployLLMSnapshotResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.delete_training_job_labels_with_options(training_job_id, request, headers, runtime) + return self.deploy_llmsnapshot_with_options(project_id, snapshot_id, request, headers, runtime) - async def delete_training_job_labels_async( + async def deploy_llmsnapshot_async( self, - training_job_id: str, - request: pai_studio_20220112_models.DeleteTrainingJobLabelsRequest, - ) -> pai_studio_20220112_models.DeleteTrainingJobLabelsResponse: + project_id: str, + snapshot_id: str, + request: pai_studio_20220112_models.DeployLLMSnapshotRequest, + ) -> pai_studio_20220112_models.DeployLLMSnapshotResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.delete_training_job_labels_with_options_async(training_job_id, request, headers, runtime) + return await self.deploy_llmsnapshot_with_options_async(project_id, snapshot_id, request, headers, runtime) def get_ai4ddefault_bucket_with_options( self, @@ -2428,14 +3226,236 @@ async def get_jobs_statistics_by_quota_with_options_async( if not UtilClient.is_unset(request.workspace_id): query['WorkspaceId'] = request.workspace_id req = open_api_models.OpenApiRequest( - headers=headers, - query=OpenApiUtilClient.query(query) + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='GetJobsStatisticsByQuota', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/quotas/{OpenApiUtilClient.get_encode_param(quota_id)}/statistics/jobs', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.GetJobsStatisticsByQuotaResponse(), + await self.call_api_async(params, req, runtime) + ) + + def get_jobs_statistics_by_quota( + self, + quota_id: str, + request: pai_studio_20220112_models.GetJobsStatisticsByQuotaRequest, + ) -> pai_studio_20220112_models.GetJobsStatisticsByQuotaResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.get_jobs_statistics_by_quota_with_options(quota_id, request, headers, runtime) + + async def get_jobs_statistics_by_quota_async( + self, + quota_id: str, + request: pai_studio_20220112_models.GetJobsStatisticsByQuotaRequest, + ) -> pai_studio_20220112_models.GetJobsStatisticsByQuotaResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.get_jobs_statistics_by_quota_with_options_async(quota_id, request, headers, runtime) + + def get_jobs_statistics_by_resource_group_with_options( + self, + resource_group_id: str, + request: pai_studio_20220112_models.GetJobsStatisticsByResourceGroupRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.GetJobsStatisticsByResourceGroupResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.end_time): + query['EndTime'] = request.end_time + if not UtilClient.is_unset(request.start_time): + query['StartTime'] = request.start_time + if not UtilClient.is_unset(request.workspace_id): + query['WorkspaceID'] = request.workspace_id + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='GetJobsStatisticsByResourceGroup', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/resources/{OpenApiUtilClient.get_encode_param(resource_group_id)}/statistics/jobs', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.GetJobsStatisticsByResourceGroupResponse(), + self.call_api(params, req, runtime) + ) + + async def get_jobs_statistics_by_resource_group_with_options_async( + self, + resource_group_id: str, + request: pai_studio_20220112_models.GetJobsStatisticsByResourceGroupRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.GetJobsStatisticsByResourceGroupResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.end_time): + query['EndTime'] = request.end_time + if not UtilClient.is_unset(request.start_time): + query['StartTime'] = request.start_time + if not UtilClient.is_unset(request.workspace_id): + query['WorkspaceID'] = request.workspace_id + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='GetJobsStatisticsByResourceGroup', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/resources/{OpenApiUtilClient.get_encode_param(resource_group_id)}/statistics/jobs', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.GetJobsStatisticsByResourceGroupResponse(), + await self.call_api_async(params, req, runtime) + ) + + def get_jobs_statistics_by_resource_group( + self, + resource_group_id: str, + request: pai_studio_20220112_models.GetJobsStatisticsByResourceGroupRequest, + ) -> pai_studio_20220112_models.GetJobsStatisticsByResourceGroupResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.get_jobs_statistics_by_resource_group_with_options(resource_group_id, request, headers, runtime) + + async def get_jobs_statistics_by_resource_group_async( + self, + resource_group_id: str, + request: pai_studio_20220112_models.GetJobsStatisticsByResourceGroupRequest, + ) -> pai_studio_20220112_models.GetJobsStatisticsByResourceGroupResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.get_jobs_statistics_by_resource_group_with_options_async(resource_group_id, request, headers, runtime) + + def get_llmproject_with_options( + self, + project_id: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.GetLLMProjectResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='GetLLMProject', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/langstudio/projects/{OpenApiUtilClient.get_encode_param(project_id)}', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.GetLLMProjectResponse(), + self.call_api(params, req, runtime) + ) + + async def get_llmproject_with_options_async( + self, + project_id: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.GetLLMProjectResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='GetLLMProject', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/langstudio/projects/{OpenApiUtilClient.get_encode_param(project_id)}', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.GetLLMProjectResponse(), + await self.call_api_async(params, req, runtime) + ) + + def get_llmproject( + self, + project_id: str, + ) -> pai_studio_20220112_models.GetLLMProjectResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.get_llmproject_with_options(project_id, headers, runtime) + + async def get_llmproject_async( + self, + project_id: str, + ) -> pai_studio_20220112_models.GetLLMProjectResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.get_llmproject_with_options_async(project_id, headers, runtime) + + def get_llmservice_identity_role_with_options( + self, + role_name: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.GetLLMServiceIdentityRoleResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='GetLLMServiceIdentityRole', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/langstudio/serviceidentityroles/{OpenApiUtilClient.get_encode_param(role_name)}', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.GetLLMServiceIdentityRoleResponse(), + self.call_api(params, req, runtime) + ) + + async def get_llmservice_identity_role_with_options_async( + self, + role_name: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.GetLLMServiceIdentityRoleResponse: + req = open_api_models.OpenApiRequest( + headers=headers ) params = open_api_models.Params( - action='GetJobsStatisticsByQuota', + action='GetLLMServiceIdentityRole', version='2022-01-12', protocol='HTTPS', - pathname=f'/api/v1/quotas/{OpenApiUtilClient.get_encode_param(quota_id)}/statistics/jobs', + pathname=f'/api/v1/langstudio/serviceidentityroles/{OpenApiUtilClient.get_encode_param(role_name)}', method='GET', auth_type='AK', style='ROA', @@ -2443,52 +3463,41 @@ async def get_jobs_statistics_by_quota_with_options_async( body_type='json' ) return TeaCore.from_map( - pai_studio_20220112_models.GetJobsStatisticsByQuotaResponse(), + pai_studio_20220112_models.GetLLMServiceIdentityRoleResponse(), await self.call_api_async(params, req, runtime) ) - def get_jobs_statistics_by_quota( + def get_llmservice_identity_role( self, - quota_id: str, - request: pai_studio_20220112_models.GetJobsStatisticsByQuotaRequest, - ) -> pai_studio_20220112_models.GetJobsStatisticsByQuotaResponse: + role_name: str, + ) -> pai_studio_20220112_models.GetLLMServiceIdentityRoleResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.get_jobs_statistics_by_quota_with_options(quota_id, request, headers, runtime) + return self.get_llmservice_identity_role_with_options(role_name, headers, runtime) - async def get_jobs_statistics_by_quota_async( + async def get_llmservice_identity_role_async( self, - quota_id: str, - request: pai_studio_20220112_models.GetJobsStatisticsByQuotaRequest, - ) -> pai_studio_20220112_models.GetJobsStatisticsByQuotaResponse: + role_name: str, + ) -> pai_studio_20220112_models.GetLLMServiceIdentityRoleResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.get_jobs_statistics_by_quota_with_options_async(quota_id, request, headers, runtime) + return await self.get_llmservice_identity_role_with_options_async(role_name, headers, runtime) - def get_jobs_statistics_by_resource_group_with_options( + def get_llmsnapshot_with_options( self, - resource_group_id: str, - request: pai_studio_20220112_models.GetJobsStatisticsByResourceGroupRequest, + project_id: str, + snapshot_id: str, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.GetJobsStatisticsByResourceGroupResponse: - UtilClient.validate_model(request) - query = {} - if not UtilClient.is_unset(request.end_time): - query['EndTime'] = request.end_time - if not UtilClient.is_unset(request.start_time): - query['StartTime'] = request.start_time - if not UtilClient.is_unset(request.workspace_id): - query['WorkspaceID'] = request.workspace_id + ) -> pai_studio_20220112_models.GetLLMSnapshotResponse: req = open_api_models.OpenApiRequest( - headers=headers, - query=OpenApiUtilClient.query(query) + headers=headers ) params = open_api_models.Params( - action='GetJobsStatisticsByResourceGroup', + action='GetLLMSnapshot', version='2022-01-12', protocol='HTTPS', - pathname=f'/api/v1/resources/{OpenApiUtilClient.get_encode_param(resource_group_id)}/statistics/jobs', + pathname=f'/api/v1/langstudio/projects/{OpenApiUtilClient.get_encode_param(project_id)}/snapshots/{OpenApiUtilClient.get_encode_param(snapshot_id)}', method='GET', auth_type='AK', style='ROA', @@ -2496,34 +3505,25 @@ def get_jobs_statistics_by_resource_group_with_options( body_type='json' ) return TeaCore.from_map( - pai_studio_20220112_models.GetJobsStatisticsByResourceGroupResponse(), + pai_studio_20220112_models.GetLLMSnapshotResponse(), self.call_api(params, req, runtime) ) - async def get_jobs_statistics_by_resource_group_with_options_async( + async def get_llmsnapshot_with_options_async( self, - resource_group_id: str, - request: pai_studio_20220112_models.GetJobsStatisticsByResourceGroupRequest, + project_id: str, + snapshot_id: str, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.GetJobsStatisticsByResourceGroupResponse: - UtilClient.validate_model(request) - query = {} - if not UtilClient.is_unset(request.end_time): - query['EndTime'] = request.end_time - if not UtilClient.is_unset(request.start_time): - query['StartTime'] = request.start_time - if not UtilClient.is_unset(request.workspace_id): - query['WorkspaceID'] = request.workspace_id + ) -> pai_studio_20220112_models.GetLLMSnapshotResponse: req = open_api_models.OpenApiRequest( - headers=headers, - query=OpenApiUtilClient.query(query) + headers=headers ) params = open_api_models.Params( - action='GetJobsStatisticsByResourceGroup', + action='GetLLMSnapshot', version='2022-01-12', protocol='HTTPS', - pathname=f'/api/v1/resources/{OpenApiUtilClient.get_encode_param(resource_group_id)}/statistics/jobs', + pathname=f'/api/v1/langstudio/projects/{OpenApiUtilClient.get_encode_param(project_id)}/snapshots/{OpenApiUtilClient.get_encode_param(snapshot_id)}', method='GET', auth_type='AK', style='ROA', @@ -2531,27 +3531,27 @@ async def get_jobs_statistics_by_resource_group_with_options_async( body_type='json' ) return TeaCore.from_map( - pai_studio_20220112_models.GetJobsStatisticsByResourceGroupResponse(), + pai_studio_20220112_models.GetLLMSnapshotResponse(), await self.call_api_async(params, req, runtime) ) - def get_jobs_statistics_by_resource_group( + def get_llmsnapshot( self, - resource_group_id: str, - request: pai_studio_20220112_models.GetJobsStatisticsByResourceGroupRequest, - ) -> pai_studio_20220112_models.GetJobsStatisticsByResourceGroupResponse: + project_id: str, + snapshot_id: str, + ) -> pai_studio_20220112_models.GetLLMSnapshotResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.get_jobs_statistics_by_resource_group_with_options(resource_group_id, request, headers, runtime) + return self.get_llmsnapshot_with_options(project_id, snapshot_id, headers, runtime) - async def get_jobs_statistics_by_resource_group_async( + async def get_llmsnapshot_async( self, - resource_group_id: str, - request: pai_studio_20220112_models.GetJobsStatisticsByResourceGroupRequest, - ) -> pai_studio_20220112_models.GetJobsStatisticsByResourceGroupResponse: + project_id: str, + snapshot_id: str, + ) -> pai_studio_20220112_models.GetLLMSnapshotResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.get_jobs_statistics_by_resource_group_with_options_async(resource_group_id, request, headers, runtime) + return await self.get_llmsnapshot_with_options_async(project_id, snapshot_id, headers, runtime) def get_machine_group_with_options( self, @@ -2881,6 +3881,110 @@ async def get_operation_async( headers = {} return await self.get_operation_with_options_async(operation_id, headers, runtime) + def get_queue_infos_with_options( + self, + request: pai_studio_20220112_models.GetQueueInfosRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.GetQueueInfosResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.order): + query['Order'] = request.order + if not UtilClient.is_unset(request.page_number): + query['PageNumber'] = request.page_number + if not UtilClient.is_unset(request.page_size): + query['PageSize'] = request.page_size + if not UtilClient.is_unset(request.quota_ids): + query['QuotaIds'] = request.quota_ids + if not UtilClient.is_unset(request.sort_by): + query['SortBy'] = request.sort_by + if not UtilClient.is_unset(request.workload_ids): + query['WorkloadIds'] = request.workload_ids + if not UtilClient.is_unset(request.workload_type): + query['WorkloadType'] = request.workload_type + if not UtilClient.is_unset(request.workspace_ids): + query['WorkspaceIds'] = request.workspace_ids + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='GetQueueInfos', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/queueInfos', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.GetQueueInfosResponse(), + self.call_api(params, req, runtime) + ) + + async def get_queue_infos_with_options_async( + self, + request: pai_studio_20220112_models.GetQueueInfosRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.GetQueueInfosResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.order): + query['Order'] = request.order + if not UtilClient.is_unset(request.page_number): + query['PageNumber'] = request.page_number + if not UtilClient.is_unset(request.page_size): + query['PageSize'] = request.page_size + if not UtilClient.is_unset(request.quota_ids): + query['QuotaIds'] = request.quota_ids + if not UtilClient.is_unset(request.sort_by): + query['SortBy'] = request.sort_by + if not UtilClient.is_unset(request.workload_ids): + query['WorkloadIds'] = request.workload_ids + if not UtilClient.is_unset(request.workload_type): + query['WorkloadType'] = request.workload_type + if not UtilClient.is_unset(request.workspace_ids): + query['WorkspaceIds'] = request.workspace_ids + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='GetQueueInfos', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/queueInfos', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.GetQueueInfosResponse(), + await self.call_api_async(params, req, runtime) + ) + + def get_queue_infos( + self, + request: pai_studio_20220112_models.GetQueueInfosRequest, + ) -> pai_studio_20220112_models.GetQueueInfosResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.get_queue_infos_with_options(request, headers, runtime) + + async def get_queue_infos_async( + self, + request: pai_studio_20220112_models.GetQueueInfosRequest, + ) -> pai_studio_20220112_models.GetQueueInfosResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.get_queue_infos_with_options_async(request, headers, runtime) + def get_quota_with_options( self, quota_id: str, @@ -3262,10 +4366,22 @@ def get_quota_node_view_metrics_with_options( query = {} if not UtilClient.is_unset(request.node_id): query['NodeId'] = request.node_id + if not UtilClient.is_unset(request.node_status): + query['NodeStatus'] = request.node_status + if not UtilClient.is_unset(request.order): + query['Order'] = request.order + if not UtilClient.is_unset(request.order_status): + query['OrderStatus'] = request.order_status if not UtilClient.is_unset(request.page_number): query['PageNumber'] = request.page_number if not UtilClient.is_unset(request.page_size): query['PageSize'] = request.page_size + if not UtilClient.is_unset(request.resource_group_id): + query['ResourceGroupId'] = request.resource_group_id + if not UtilClient.is_unset(request.self_only): + query['SelfOnly'] = request.self_only + if not UtilClient.is_unset(request.sort_by): + query['SortBy'] = request.sort_by if not UtilClient.is_unset(request.time_step): query['TimeStep'] = request.time_step if not UtilClient.is_unset(request.workspace_id): @@ -3301,10 +4417,22 @@ async def get_quota_node_view_metrics_with_options_async( query = {} if not UtilClient.is_unset(request.node_id): query['NodeId'] = request.node_id + if not UtilClient.is_unset(request.node_status): + query['NodeStatus'] = request.node_status + if not UtilClient.is_unset(request.order): + query['Order'] = request.order + if not UtilClient.is_unset(request.order_status): + query['OrderStatus'] = request.order_status if not UtilClient.is_unset(request.page_number): query['PageNumber'] = request.page_number if not UtilClient.is_unset(request.page_size): query['PageSize'] = request.page_size + if not UtilClient.is_unset(request.resource_group_id): + query['ResourceGroupId'] = request.resource_group_id + if not UtilClient.is_unset(request.self_only): + query['SelfOnly'] = request.self_only + if not UtilClient.is_unset(request.sort_by): + query['SortBy'] = request.sort_by if not UtilClient.is_unset(request.time_step): query['TimeStep'] = request.time_step if not UtilClient.is_unset(request.workspace_id): @@ -3347,6 +4475,126 @@ async def get_quota_node_view_metrics_async( headers = {} return await self.get_quota_node_view_metrics_with_options_async(quota_id, request, headers, runtime) + def get_quota_queue_info_with_options( + self, + quota_id: str, + request: pai_studio_20220112_models.GetQuotaQueueInfoRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.GetQuotaQueueInfoResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.before_workload_id): + query['BeforeWorkloadId'] = request.before_workload_id + if not UtilClient.is_unset(request.order): + query['Order'] = request.order + if not UtilClient.is_unset(request.page_number): + query['PageNumber'] = request.page_number + if not UtilClient.is_unset(request.page_size): + query['PageSize'] = request.page_size + if not UtilClient.is_unset(request.show_own): + query['ShowOwn'] = request.show_own + if not UtilClient.is_unset(request.sort_by): + query['SortBy'] = request.sort_by + if not UtilClient.is_unset(request.status): + query['Status'] = request.status + if not UtilClient.is_unset(request.sub_quota_ids): + query['SubQuotaIds'] = request.sub_quota_ids + if not UtilClient.is_unset(request.workload_ids): + query['WorkloadIds'] = request.workload_ids + if not UtilClient.is_unset(request.workload_type): + query['WorkloadType'] = request.workload_type + if not UtilClient.is_unset(request.workspace_ids): + query['WorkspaceIds'] = request.workspace_ids + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='GetQuotaQueueInfo', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/quotas/{OpenApiUtilClient.get_encode_param(quota_id)}/queueinfos', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.GetQuotaQueueInfoResponse(), + self.call_api(params, req, runtime) + ) + + async def get_quota_queue_info_with_options_async( + self, + quota_id: str, + request: pai_studio_20220112_models.GetQuotaQueueInfoRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.GetQuotaQueueInfoResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.before_workload_id): + query['BeforeWorkloadId'] = request.before_workload_id + if not UtilClient.is_unset(request.order): + query['Order'] = request.order + if not UtilClient.is_unset(request.page_number): + query['PageNumber'] = request.page_number + if not UtilClient.is_unset(request.page_size): + query['PageSize'] = request.page_size + if not UtilClient.is_unset(request.show_own): + query['ShowOwn'] = request.show_own + if not UtilClient.is_unset(request.sort_by): + query['SortBy'] = request.sort_by + if not UtilClient.is_unset(request.status): + query['Status'] = request.status + if not UtilClient.is_unset(request.sub_quota_ids): + query['SubQuotaIds'] = request.sub_quota_ids + if not UtilClient.is_unset(request.workload_ids): + query['WorkloadIds'] = request.workload_ids + if not UtilClient.is_unset(request.workload_type): + query['WorkloadType'] = request.workload_type + if not UtilClient.is_unset(request.workspace_ids): + query['WorkspaceIds'] = request.workspace_ids + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='GetQuotaQueueInfo', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/quotas/{OpenApiUtilClient.get_encode_param(quota_id)}/queueinfos', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.GetQuotaQueueInfoResponse(), + await self.call_api_async(params, req, runtime) + ) + + def get_quota_queue_info( + self, + quota_id: str, + request: pai_studio_20220112_models.GetQuotaQueueInfoRequest, + ) -> pai_studio_20220112_models.GetQuotaQueueInfoResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.get_quota_queue_info_with_options(quota_id, request, headers, runtime) + + async def get_quota_queue_info_async( + self, + quota_id: str, + request: pai_studio_20220112_models.GetQuotaQueueInfoRequest, + ) -> pai_studio_20220112_models.GetQuotaQueueInfoResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.get_quota_queue_info_with_options_async(quota_id, request, headers, runtime) + def get_quota_range_user_view_metrics_with_options( self, quota_id: str, @@ -3670,14 +4918,20 @@ async def get_range_user_view_metrics_async( def get_resource_group_with_options( self, resource_group_id: str, - request: pai_studio_20220112_models.GetResourceGroupRequest, + tmp_req: pai_studio_20220112_models.GetResourceGroupRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetResourceGroupResponse: - UtilClient.validate_model(request) + UtilClient.validate_model(tmp_req) + request = pai_studio_20220112_models.GetResourceGroupShrinkRequest() + OpenApiUtilClient.convert(tmp_req, request) + if not UtilClient.is_unset(tmp_req.tag): + request.tag_shrink = OpenApiUtilClient.array_to_string_with_specified_style(tmp_req.tag, 'Tag', 'json') query = {} if not UtilClient.is_unset(request.is_aiworkspace_data_enabled): query['IsAIWorkspaceDataEnabled'] = request.is_aiworkspace_data_enabled + if not UtilClient.is_unset(request.tag_shrink): + query['Tag'] = request.tag_shrink req = open_api_models.OpenApiRequest( headers=headers, query=OpenApiUtilClient.query(query) @@ -3701,14 +4955,20 @@ def get_resource_group_with_options( async def get_resource_group_with_options_async( self, resource_group_id: str, - request: pai_studio_20220112_models.GetResourceGroupRequest, + tmp_req: pai_studio_20220112_models.GetResourceGroupRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetResourceGroupResponse: - UtilClient.validate_model(request) + UtilClient.validate_model(tmp_req) + request = pai_studio_20220112_models.GetResourceGroupShrinkRequest() + OpenApiUtilClient.convert(tmp_req, request) + if not UtilClient.is_unset(tmp_req.tag): + request.tag_shrink = OpenApiUtilClient.array_to_string_with_specified_style(tmp_req.tag, 'Tag', 'json') query = {} if not UtilClient.is_unset(request.is_aiworkspace_data_enabled): query['IsAIWorkspaceDataEnabled'] = request.is_aiworkspace_data_enabled + if not UtilClient.is_unset(request.tag_shrink): + query['Tag'] = request.tag_shrink req = open_api_models.OpenApiRequest( headers=headers, query=OpenApiUtilClient.query(query) @@ -3751,11 +5011,21 @@ def get_resource_group_machine_group_with_options( self, machine_group_id: str, resource_group_id: str, + tmp_req: pai_studio_20220112_models.GetResourceGroupMachineGroupRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetResourceGroupMachineGroupResponse: + UtilClient.validate_model(tmp_req) + request = pai_studio_20220112_models.GetResourceGroupMachineGroupShrinkRequest() + OpenApiUtilClient.convert(tmp_req, request) + if not UtilClient.is_unset(tmp_req.tag): + request.tag_shrink = OpenApiUtilClient.array_to_string_with_specified_style(tmp_req.tag, 'Tag', 'json') + query = {} + if not UtilClient.is_unset(request.tag_shrink): + query['Tag'] = request.tag_shrink req = open_api_models.OpenApiRequest( - headers=headers + headers=headers, + query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( action='GetResourceGroupMachineGroup', @@ -3777,11 +5047,21 @@ async def get_resource_group_machine_group_with_options_async( self, machine_group_id: str, resource_group_id: str, + tmp_req: pai_studio_20220112_models.GetResourceGroupMachineGroupRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetResourceGroupMachineGroupResponse: + UtilClient.validate_model(tmp_req) + request = pai_studio_20220112_models.GetResourceGroupMachineGroupShrinkRequest() + OpenApiUtilClient.convert(tmp_req, request) + if not UtilClient.is_unset(tmp_req.tag): + request.tag_shrink = OpenApiUtilClient.array_to_string_with_specified_style(tmp_req.tag, 'Tag', 'json') + query = {} + if not UtilClient.is_unset(request.tag_shrink): + query['Tag'] = request.tag_shrink req = open_api_models.OpenApiRequest( - headers=headers + headers=headers, + query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( action='GetResourceGroupMachineGroup', @@ -3803,19 +5083,21 @@ def get_resource_group_machine_group( self, machine_group_id: str, resource_group_id: str, + request: pai_studio_20220112_models.GetResourceGroupMachineGroupRequest, ) -> pai_studio_20220112_models.GetResourceGroupMachineGroupResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.get_resource_group_machine_group_with_options(machine_group_id, resource_group_id, headers, runtime) + return self.get_resource_group_machine_group_with_options(machine_group_id, resource_group_id, request, headers, runtime) async def get_resource_group_machine_group_async( self, machine_group_id: str, resource_group_id: str, + request: pai_studio_20220112_models.GetResourceGroupMachineGroupRequest, ) -> pai_studio_20220112_models.GetResourceGroupMachineGroupResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.get_resource_group_machine_group_with_options_async(machine_group_id, resource_group_id, headers, runtime) + return await self.get_resource_group_machine_group_with_options_async(machine_group_id, resource_group_id, request, headers, runtime) def get_resource_group_metrics_with_options( self, @@ -4135,6 +5417,172 @@ async def get_service_identity_role_async( headers = {} return await self.get_service_identity_role_with_options_async(role_name, headers, runtime) + def get_spot_price_history_with_options( + self, + instance_type: str, + request: pai_studio_20220112_models.GetSpotPriceHistoryRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.GetSpotPriceHistoryResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.end_time): + query['EndTime'] = request.end_time + if not UtilClient.is_unset(request.order): + query['Order'] = request.order + if not UtilClient.is_unset(request.page_number): + query['PageNumber'] = request.page_number + if not UtilClient.is_unset(request.page_size): + query['PageSize'] = request.page_size + if not UtilClient.is_unset(request.sort_by): + query['SortBy'] = request.sort_by + if not UtilClient.is_unset(request.start_time): + query['StartTime'] = request.start_time + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='GetSpotPriceHistory', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/spots/{OpenApiUtilClient.get_encode_param(instance_type)}/pricehistory', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.GetSpotPriceHistoryResponse(), + self.call_api(params, req, runtime) + ) + + async def get_spot_price_history_with_options_async( + self, + instance_type: str, + request: pai_studio_20220112_models.GetSpotPriceHistoryRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.GetSpotPriceHistoryResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.end_time): + query['EndTime'] = request.end_time + if not UtilClient.is_unset(request.order): + query['Order'] = request.order + if not UtilClient.is_unset(request.page_number): + query['PageNumber'] = request.page_number + if not UtilClient.is_unset(request.page_size): + query['PageSize'] = request.page_size + if not UtilClient.is_unset(request.sort_by): + query['SortBy'] = request.sort_by + if not UtilClient.is_unset(request.start_time): + query['StartTime'] = request.start_time + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='GetSpotPriceHistory', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/spots/{OpenApiUtilClient.get_encode_param(instance_type)}/pricehistory', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.GetSpotPriceHistoryResponse(), + await self.call_api_async(params, req, runtime) + ) + + def get_spot_price_history( + self, + instance_type: str, + request: pai_studio_20220112_models.GetSpotPriceHistoryRequest, + ) -> pai_studio_20220112_models.GetSpotPriceHistoryResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.get_spot_price_history_with_options(instance_type, request, headers, runtime) + + async def get_spot_price_history_async( + self, + instance_type: str, + request: pai_studio_20220112_models.GetSpotPriceHistoryRequest, + ) -> pai_studio_20220112_models.GetSpotPriceHistoryResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.get_spot_price_history_with_options_async(instance_type, request, headers, runtime) + + def get_spot_stock_preview_with_options( + self, + instance_type: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.GetSpotStockPreviewResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='GetSpotStockPreview', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/spots/{OpenApiUtilClient.get_encode_param(instance_type)}/stockpreview', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.GetSpotStockPreviewResponse(), + self.call_api(params, req, runtime) + ) + + async def get_spot_stock_preview_with_options_async( + self, + instance_type: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.GetSpotStockPreviewResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='GetSpotStockPreview', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/spots/{OpenApiUtilClient.get_encode_param(instance_type)}/stockpreview', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.GetSpotStockPreviewResponse(), + await self.call_api_async(params, req, runtime) + ) + + def get_spot_stock_preview( + self, + instance_type: str, + ) -> pai_studio_20220112_models.GetSpotStockPreviewResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.get_spot_stock_preview_with_options(instance_type, headers, runtime) + + async def get_spot_stock_preview_async( + self, + instance_type: str, + ) -> pai_studio_20220112_models.GetSpotStockPreviewResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.get_spot_stock_preview_with_options_async(instance_type, headers, runtime) + def get_token_with_options( self, request: pai_studio_20220112_models.GetTokenRequest, @@ -4295,6 +5743,86 @@ async def get_training_job_async( headers = {} return await self.get_training_job_with_options_async(training_job_id, request, headers, runtime) + def get_training_job_error_info_with_options( + self, + training_job_id: str, + request: pai_studio_20220112_models.GetTrainingJobErrorInfoRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.GetTrainingJobErrorInfoResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.token): + query['Token'] = request.token + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='GetTrainingJobErrorInfo', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/trainingjobs/{OpenApiUtilClient.get_encode_param(training_job_id)}/errorinfo', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.GetTrainingJobErrorInfoResponse(), + self.call_api(params, req, runtime) + ) + + async def get_training_job_error_info_with_options_async( + self, + training_job_id: str, + request: pai_studio_20220112_models.GetTrainingJobErrorInfoRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.GetTrainingJobErrorInfoResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.token): + query['Token'] = request.token + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='GetTrainingJobErrorInfo', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/trainingjobs/{OpenApiUtilClient.get_encode_param(training_job_id)}/errorinfo', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.GetTrainingJobErrorInfoResponse(), + await self.call_api_async(params, req, runtime) + ) + + def get_training_job_error_info( + self, + training_job_id: str, + request: pai_studio_20220112_models.GetTrainingJobErrorInfoRequest, + ) -> pai_studio_20220112_models.GetTrainingJobErrorInfoResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.get_training_job_error_info_with_options(training_job_id, request, headers, runtime) + + async def get_training_job_error_info_async( + self, + training_job_id: str, + request: pai_studio_20220112_models.GetTrainingJobErrorInfoRequest, + ) -> pai_studio_20220112_models.GetTrainingJobErrorInfoResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.get_training_job_error_info_with_options_async(training_job_id, request, headers, runtime) + def get_training_job_latest_metrics_with_options( self, training_job_id: str, @@ -4450,10 +5978,92 @@ async def get_user_view_metrics_with_options_async( query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='GetUserViewMetrics', + action='GetUserViewMetrics', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/resources/{OpenApiUtilClient.get_encode_param(resource_group_id)}/usermetrics', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.GetUserViewMetricsResponse(), + await self.call_api_async(params, req, runtime) + ) + + def get_user_view_metrics( + self, + resource_group_id: str, + request: pai_studio_20220112_models.GetUserViewMetricsRequest, + ) -> pai_studio_20220112_models.GetUserViewMetricsResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.get_user_view_metrics_with_options(resource_group_id, request, headers, runtime) + + async def get_user_view_metrics_async( + self, + resource_group_id: str, + request: pai_studio_20220112_models.GetUserViewMetricsRequest, + ) -> pai_studio_20220112_models.GetUserViewMetricsResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.get_user_view_metrics_with_options_async(resource_group_id, request, headers, runtime) + + def list_ai4dserivces_with_options( + self, + request: pai_studio_20220112_models.ListAI4DSerivcesRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.ListAI4DSerivcesResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.service_type): + query['ServiceType'] = request.service_type + if not UtilClient.is_unset(request.workspace_id): + query['WorkspaceId'] = request.workspace_id + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='ListAI4DSerivces', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/ai4d/services', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.ListAI4DSerivcesResponse(), + self.call_api(params, req, runtime) + ) + + async def list_ai4dserivces_with_options_async( + self, + request: pai_studio_20220112_models.ListAI4DSerivcesRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.ListAI4DSerivcesResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.service_type): + query['ServiceType'] = request.service_type + if not UtilClient.is_unset(request.workspace_id): + query['WorkspaceId'] = request.workspace_id + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='ListAI4DSerivces', version='2022-01-12', protocol='HTTPS', - pathname=f'/api/v1/resources/{OpenApiUtilClient.get_encode_param(resource_group_id)}/usermetrics', + pathname=f'/api/v1/ai4d/services', method='GET', auth_type='AK', style='ROA', @@ -4461,34 +6071,32 @@ async def get_user_view_metrics_with_options_async( body_type='json' ) return TeaCore.from_map( - pai_studio_20220112_models.GetUserViewMetricsResponse(), + pai_studio_20220112_models.ListAI4DSerivcesResponse(), await self.call_api_async(params, req, runtime) ) - def get_user_view_metrics( + def list_ai4dserivces( self, - resource_group_id: str, - request: pai_studio_20220112_models.GetUserViewMetricsRequest, - ) -> pai_studio_20220112_models.GetUserViewMetricsResponse: + request: pai_studio_20220112_models.ListAI4DSerivcesRequest, + ) -> pai_studio_20220112_models.ListAI4DSerivcesResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.get_user_view_metrics_with_options(resource_group_id, request, headers, runtime) + return self.list_ai4dserivces_with_options(request, headers, runtime) - async def get_user_view_metrics_async( + async def list_ai4dserivces_async( self, - resource_group_id: str, - request: pai_studio_20220112_models.GetUserViewMetricsRequest, - ) -> pai_studio_20220112_models.GetUserViewMetricsResponse: + request: pai_studio_20220112_models.ListAI4DSerivcesRequest, + ) -> pai_studio_20220112_models.ListAI4DSerivcesResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.get_user_view_metrics_with_options_async(resource_group_id, request, headers, runtime) + return await self.list_ai4dserivces_with_options_async(request, headers, runtime) - def list_ai4dserivces_with_options( + def list_ai4dservice_templates_with_options( self, - request: pai_studio_20220112_models.ListAI4DSerivcesRequest, + request: pai_studio_20220112_models.ListAI4DServiceTemplatesRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.ListAI4DSerivcesResponse: + ) -> pai_studio_20220112_models.ListAI4DServiceTemplatesResponse: UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.service_type): @@ -4500,10 +6108,10 @@ def list_ai4dserivces_with_options( query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='ListAI4DSerivces', + action='ListAI4DServiceTemplates', version='2022-01-12', protocol='HTTPS', - pathname=f'/api/v1/ai4d/services', + pathname=f'/api/v1/ai4d/servicetemplates', method='GET', auth_type='AK', style='ROA', @@ -4511,16 +6119,16 @@ def list_ai4dserivces_with_options( body_type='json' ) return TeaCore.from_map( - pai_studio_20220112_models.ListAI4DSerivcesResponse(), + pai_studio_20220112_models.ListAI4DServiceTemplatesResponse(), self.call_api(params, req, runtime) ) - async def list_ai4dserivces_with_options_async( + async def list_ai4dservice_templates_with_options_async( self, - request: pai_studio_20220112_models.ListAI4DSerivcesRequest, + request: pai_studio_20220112_models.ListAI4DServiceTemplatesRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.ListAI4DSerivcesResponse: + ) -> pai_studio_20220112_models.ListAI4DServiceTemplatesResponse: UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.service_type): @@ -4532,10 +6140,10 @@ async def list_ai4dserivces_with_options_async( query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='ListAI4DSerivces', + action='ListAI4DServiceTemplates', version='2022-01-12', protocol='HTTPS', - pathname=f'/api/v1/ai4d/services', + pathname=f'/api/v1/ai4d/servicetemplates', method='GET', auth_type='AK', style='ROA', @@ -4543,25 +6151,25 @@ async def list_ai4dserivces_with_options_async( body_type='json' ) return TeaCore.from_map( - pai_studio_20220112_models.ListAI4DSerivcesResponse(), + pai_studio_20220112_models.ListAI4DServiceTemplatesResponse(), await self.call_api_async(params, req, runtime) ) - def list_ai4dserivces( + def list_ai4dservice_templates( self, - request: pai_studio_20220112_models.ListAI4DSerivcesRequest, - ) -> pai_studio_20220112_models.ListAI4DSerivcesResponse: + request: pai_studio_20220112_models.ListAI4DServiceTemplatesRequest, + ) -> pai_studio_20220112_models.ListAI4DServiceTemplatesResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.list_ai4dserivces_with_options(request, headers, runtime) + return self.list_ai4dservice_templates_with_options(request, headers, runtime) - async def list_ai4dserivces_async( + async def list_ai4dservice_templates_async( self, - request: pai_studio_20220112_models.ListAI4DSerivcesRequest, - ) -> pai_studio_20220112_models.ListAI4DSerivcesResponse: + request: pai_studio_20220112_models.ListAI4DServiceTemplatesRequest, + ) -> pai_studio_20220112_models.ListAI4DServiceTemplatesResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.list_ai4dserivces_with_options_async(request, headers, runtime) + return await self.list_ai4dservice_templates_with_options_async(request, headers, runtime) def list_algorithm_versions_with_options( self, @@ -5139,7 +6747,273 @@ async def list_instance_jobs_with_options_async( action='ListInstanceJobs', version='2022-01-12', protocol='HTTPS', - pathname=f'/api/v1/instancejobs', + pathname=f'/api/v1/instancejobs', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.ListInstanceJobsResponse(), + await self.call_api_async(params, req, runtime) + ) + + def list_instance_jobs( + self, + request: pai_studio_20220112_models.ListInstanceJobsRequest, + ) -> pai_studio_20220112_models.ListInstanceJobsResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.list_instance_jobs_with_options(request, headers, runtime) + + async def list_instance_jobs_async( + self, + request: pai_studio_20220112_models.ListInstanceJobsRequest, + ) -> pai_studio_20220112_models.ListInstanceJobsResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.list_instance_jobs_with_options_async(request, headers, runtime) + + def list_llmprojects_with_options( + self, + request: pai_studio_20220112_models.ListLLMProjectsRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.ListLLMProjectsResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.order): + query['Order'] = request.order + if not UtilClient.is_unset(request.page_number): + query['PageNumber'] = request.page_number + if not UtilClient.is_unset(request.page_size): + query['PageSize'] = request.page_size + if not UtilClient.is_unset(request.project_name): + query['ProjectName'] = request.project_name + if not UtilClient.is_unset(request.sort_by): + query['SortBy'] = request.sort_by + if not UtilClient.is_unset(request.workspace_id): + query['WorkspaceId'] = request.workspace_id + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='ListLLMProjects', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/langstudio/projects', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.ListLLMProjectsResponse(), + self.call_api(params, req, runtime) + ) + + async def list_llmprojects_with_options_async( + self, + request: pai_studio_20220112_models.ListLLMProjectsRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.ListLLMProjectsResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.order): + query['Order'] = request.order + if not UtilClient.is_unset(request.page_number): + query['PageNumber'] = request.page_number + if not UtilClient.is_unset(request.page_size): + query['PageSize'] = request.page_size + if not UtilClient.is_unset(request.project_name): + query['ProjectName'] = request.project_name + if not UtilClient.is_unset(request.sort_by): + query['SortBy'] = request.sort_by + if not UtilClient.is_unset(request.workspace_id): + query['WorkspaceId'] = request.workspace_id + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='ListLLMProjects', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/langstudio/projects', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.ListLLMProjectsResponse(), + await self.call_api_async(params, req, runtime) + ) + + def list_llmprojects( + self, + request: pai_studio_20220112_models.ListLLMProjectsRequest, + ) -> pai_studio_20220112_models.ListLLMProjectsResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.list_llmprojects_with_options(request, headers, runtime) + + async def list_llmprojects_async( + self, + request: pai_studio_20220112_models.ListLLMProjectsRequest, + ) -> pai_studio_20220112_models.ListLLMProjectsResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.list_llmprojects_with_options_async(request, headers, runtime) + + def list_llmsnapshots_with_options( + self, + project_id: str, + request: pai_studio_20220112_models.ListLLMSnapshotsRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.ListLLMSnapshotsResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.order): + query['Order'] = request.order + if not UtilClient.is_unset(request.page_number): + query['PageNumber'] = request.page_number + if not UtilClient.is_unset(request.page_size): + query['PageSize'] = request.page_size + if not UtilClient.is_unset(request.sort_by): + query['SortBy'] = request.sort_by + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='ListLLMSnapshots', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/langstudio/projects/{OpenApiUtilClient.get_encode_param(project_id)}/snapshots', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.ListLLMSnapshotsResponse(), + self.call_api(params, req, runtime) + ) + + async def list_llmsnapshots_with_options_async( + self, + project_id: str, + request: pai_studio_20220112_models.ListLLMSnapshotsRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.ListLLMSnapshotsResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.order): + query['Order'] = request.order + if not UtilClient.is_unset(request.page_number): + query['PageNumber'] = request.page_number + if not UtilClient.is_unset(request.page_size): + query['PageSize'] = request.page_size + if not UtilClient.is_unset(request.sort_by): + query['SortBy'] = request.sort_by + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='ListLLMSnapshots', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/langstudio/projects/{OpenApiUtilClient.get_encode_param(project_id)}/snapshots', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.ListLLMSnapshotsResponse(), + await self.call_api_async(params, req, runtime) + ) + + def list_llmsnapshots( + self, + project_id: str, + request: pai_studio_20220112_models.ListLLMSnapshotsRequest, + ) -> pai_studio_20220112_models.ListLLMSnapshotsResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.list_llmsnapshots_with_options(project_id, request, headers, runtime) + + async def list_llmsnapshots_async( + self, + project_id: str, + request: pai_studio_20220112_models.ListLLMSnapshotsRequest, + ) -> pai_studio_20220112_models.ListLLMSnapshotsResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.list_llmsnapshots_with_options_async(project_id, request, headers, runtime) + + def list_node_pods_with_options( + self, + node_id: str, + request: pai_studio_20220112_models.ListNodePodsRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.ListNodePodsResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.resource_group_id): + query['ResourceGroupId'] = request.resource_group_id + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='ListNodePods', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/nodes/{OpenApiUtilClient.get_encode_param(node_id)}/Pods', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.ListNodePodsResponse(), + self.call_api(params, req, runtime) + ) + + async def list_node_pods_with_options_async( + self, + node_id: str, + request: pai_studio_20220112_models.ListNodePodsRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.ListNodePodsResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.resource_group_id): + query['ResourceGroupId'] = request.resource_group_id + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='ListNodePods', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/nodes/{OpenApiUtilClient.get_encode_param(node_id)}/Pods', method='GET', auth_type='AK', style='ROA', @@ -5147,25 +7021,27 @@ async def list_instance_jobs_with_options_async( body_type='json' ) return TeaCore.from_map( - pai_studio_20220112_models.ListInstanceJobsResponse(), + pai_studio_20220112_models.ListNodePodsResponse(), await self.call_api_async(params, req, runtime) ) - def list_instance_jobs( + def list_node_pods( self, - request: pai_studio_20220112_models.ListInstanceJobsRequest, - ) -> pai_studio_20220112_models.ListInstanceJobsResponse: + node_id: str, + request: pai_studio_20220112_models.ListNodePodsRequest, + ) -> pai_studio_20220112_models.ListNodePodsResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.list_instance_jobs_with_options(request, headers, runtime) + return self.list_node_pods_with_options(node_id, request, headers, runtime) - async def list_instance_jobs_async( + async def list_node_pods_async( self, - request: pai_studio_20220112_models.ListInstanceJobsRequest, - ) -> pai_studio_20220112_models.ListInstanceJobsResponse: + node_id: str, + request: pai_studio_20220112_models.ListNodePodsRequest, + ) -> pai_studio_20220112_models.ListNodePodsResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.list_instance_jobs_with_options_async(request, headers, runtime) + return await self.list_node_pods_with_options_async(node_id, request, headers, runtime) def list_node_types_with_options( self, @@ -5289,6 +7165,8 @@ def list_nodes_with_options( query['ResourceGroupIds'] = request.resource_group_ids if not UtilClient.is_unset(request.sort_by): query['SortBy'] = request.sort_by + if not UtilClient.is_unset(request.verbose): + query['Verbose'] = request.verbose req = open_api_models.OpenApiRequest( headers=headers, query=OpenApiUtilClient.query(query) @@ -5339,6 +7217,8 @@ async def list_nodes_with_options_async( query['ResourceGroupIds'] = request.resource_group_ids if not UtilClient.is_unset(request.sort_by): query['SortBy'] = request.sort_by + if not UtilClient.is_unset(request.verbose): + query['Verbose'] = request.verbose req = open_api_models.OpenApiRequest( headers=headers, query=OpenApiUtilClient.query(query) @@ -5549,6 +7429,10 @@ def list_quotas_with_options( ) -> pai_studio_20220112_models.ListQuotasResponse: UtilClient.validate_model(request) query = {} + if not UtilClient.is_unset(request.labels): + query['Labels'] = request.labels + if not UtilClient.is_unset(request.layout_mode): + query['LayoutMode'] = request.layout_mode if not UtilClient.is_unset(request.order): query['Order'] = request.order if not UtilClient.is_unset(request.page_number): @@ -5597,6 +7481,10 @@ async def list_quotas_with_options_async( ) -> pai_studio_20220112_models.ListQuotasResponse: UtilClient.validate_model(request) query = {} + if not UtilClient.is_unset(request.labels): + query['Labels'] = request.labels + if not UtilClient.is_unset(request.layout_mode): + query['LayoutMode'] = request.layout_mode if not UtilClient.is_unset(request.order): query['Order'] = request.order if not UtilClient.is_unset(request.page_number): @@ -5881,6 +7769,186 @@ async def list_resource_groups_async( headers = {} return await self.list_resource_groups_with_options_async(request, headers, runtime) + def list_spots_stock_preview_with_options( + self, + request: pai_studio_20220112_models.ListSpotsStockPreviewRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.ListSpotsStockPreviewResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.instance_types): + query['InstanceTypes'] = request.instance_types + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='ListSpotsStockPreview', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/spots/stockpreview', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.ListSpotsStockPreviewResponse(), + self.call_api(params, req, runtime) + ) + + async def list_spots_stock_preview_with_options_async( + self, + request: pai_studio_20220112_models.ListSpotsStockPreviewRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.ListSpotsStockPreviewResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.instance_types): + query['InstanceTypes'] = request.instance_types + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='ListSpotsStockPreview', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/spots/stockpreview', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.ListSpotsStockPreviewResponse(), + await self.call_api_async(params, req, runtime) + ) + + def list_spots_stock_preview( + self, + request: pai_studio_20220112_models.ListSpotsStockPreviewRequest, + ) -> pai_studio_20220112_models.ListSpotsStockPreviewResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.list_spots_stock_preview_with_options(request, headers, runtime) + + async def list_spots_stock_preview_async( + self, + request: pai_studio_20220112_models.ListSpotsStockPreviewRequest, + ) -> pai_studio_20220112_models.ListSpotsStockPreviewResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.list_spots_stock_preview_with_options_async(request, headers, runtime) + + def list_tag_resources_with_options( + self, + tmp_req: pai_studio_20220112_models.ListTagResourcesRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.ListTagResourcesResponse: + UtilClient.validate_model(tmp_req) + request = pai_studio_20220112_models.ListTagResourcesShrinkRequest() + OpenApiUtilClient.convert(tmp_req, request) + if not UtilClient.is_unset(tmp_req.resource_id): + request.resource_id_shrink = OpenApiUtilClient.array_to_string_with_specified_style(tmp_req.resource_id, 'ResourceId', 'json') + if not UtilClient.is_unset(tmp_req.tag): + request.tag_shrink = OpenApiUtilClient.array_to_string_with_specified_style(tmp_req.tag, 'Tag', 'json') + query = {} + if not UtilClient.is_unset(request.next_token): + query['NextToken'] = request.next_token + if not UtilClient.is_unset(request.region_id): + query['RegionId'] = request.region_id + if not UtilClient.is_unset(request.resource_id_shrink): + query['ResourceId'] = request.resource_id_shrink + if not UtilClient.is_unset(request.resource_type): + query['ResourceType'] = request.resource_type + if not UtilClient.is_unset(request.tag_shrink): + query['Tag'] = request.tag_shrink + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='ListTagResources', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/tags', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.ListTagResourcesResponse(), + self.call_api(params, req, runtime) + ) + + async def list_tag_resources_with_options_async( + self, + tmp_req: pai_studio_20220112_models.ListTagResourcesRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.ListTagResourcesResponse: + UtilClient.validate_model(tmp_req) + request = pai_studio_20220112_models.ListTagResourcesShrinkRequest() + OpenApiUtilClient.convert(tmp_req, request) + if not UtilClient.is_unset(tmp_req.resource_id): + request.resource_id_shrink = OpenApiUtilClient.array_to_string_with_specified_style(tmp_req.resource_id, 'ResourceId', 'json') + if not UtilClient.is_unset(tmp_req.tag): + request.tag_shrink = OpenApiUtilClient.array_to_string_with_specified_style(tmp_req.tag, 'Tag', 'json') + query = {} + if not UtilClient.is_unset(request.next_token): + query['NextToken'] = request.next_token + if not UtilClient.is_unset(request.region_id): + query['RegionId'] = request.region_id + if not UtilClient.is_unset(request.resource_id_shrink): + query['ResourceId'] = request.resource_id_shrink + if not UtilClient.is_unset(request.resource_type): + query['ResourceType'] = request.resource_type + if not UtilClient.is_unset(request.tag_shrink): + query['Tag'] = request.tag_shrink + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='ListTagResources', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/tags', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.ListTagResourcesResponse(), + await self.call_api_async(params, req, runtime) + ) + + def list_tag_resources( + self, + request: pai_studio_20220112_models.ListTagResourcesRequest, + ) -> pai_studio_20220112_models.ListTagResourcesResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.list_tag_resources_with_options(request, headers, runtime) + + async def list_tag_resources_async( + self, + request: pai_studio_20220112_models.ListTagResourcesRequest, + ) -> pai_studio_20220112_models.ListTagResourcesResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.list_tag_resources_with_options_async(request, headers, runtime) + def list_training_job_events_with_options( self, training_job_id: str, @@ -6188,6 +8256,8 @@ def list_training_job_logs_with_options( query = {} if not UtilClient.is_unset(request.end_time): query['EndTime'] = request.end_time + if not UtilClient.is_unset(request.instance_id): + query['InstanceId'] = request.instance_id if not UtilClient.is_unset(request.page_number): query['PageNumber'] = request.page_number if not UtilClient.is_unset(request.page_size): @@ -6229,6 +8299,8 @@ async def list_training_job_logs_with_options_async( query = {} if not UtilClient.is_unset(request.end_time): query['EndTime'] = request.end_time + if not UtilClient.is_unset(request.instance_id): + query['InstanceId'] = request.instance_id if not UtilClient.is_unset(request.page_number): query['PageNumber'] = request.page_number if not UtilClient.is_unset(request.page_size): @@ -6381,6 +8453,86 @@ async def list_training_job_metrics_async( headers = {} return await self.list_training_job_metrics_with_options_async(training_job_id, request, headers, runtime) + def list_training_job_output_models_with_options( + self, + training_job_id: str, + request: pai_studio_20220112_models.ListTrainingJobOutputModelsRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.ListTrainingJobOutputModelsResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.token): + query['Token'] = request.token + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='ListTrainingJobOutputModels', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/trainingjobs/{OpenApiUtilClient.get_encode_param(training_job_id)}/outputmodels', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.ListTrainingJobOutputModelsResponse(), + self.call_api(params, req, runtime) + ) + + async def list_training_job_output_models_with_options_async( + self, + training_job_id: str, + request: pai_studio_20220112_models.ListTrainingJobOutputModelsRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.ListTrainingJobOutputModelsResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.token): + query['Token'] = request.token + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='ListTrainingJobOutputModels', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/trainingjobs/{OpenApiUtilClient.get_encode_param(training_job_id)}/outputmodels', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.ListTrainingJobOutputModelsResponse(), + await self.call_api_async(params, req, runtime) + ) + + def list_training_job_output_models( + self, + training_job_id: str, + request: pai_studio_20220112_models.ListTrainingJobOutputModelsRequest, + ) -> pai_studio_20220112_models.ListTrainingJobOutputModelsResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.list_training_job_output_models_with_options(training_job_id, request, headers, runtime) + + async def list_training_job_output_models_async( + self, + training_job_id: str, + request: pai_studio_20220112_models.ListTrainingJobOutputModelsRequest, + ) -> pai_studio_20220112_models.ListTrainingJobOutputModelsResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.list_training_job_output_models_with_options_async(training_job_id, request, headers, runtime) + def list_training_jobs_with_options( self, tmp_req: pai_studio_20220112_models.ListTrainingJobsRequest, @@ -6486,36 +8638,120 @@ async def list_training_jobs_with_options_async( query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='ListTrainingJobs', + action='ListTrainingJobs', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/trainingjobs', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.ListTrainingJobsResponse(), + await self.call_api_async(params, req, runtime) + ) + + def list_training_jobs( + self, + request: pai_studio_20220112_models.ListTrainingJobsRequest, + ) -> pai_studio_20220112_models.ListTrainingJobsResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.list_training_jobs_with_options(request, headers, runtime) + + async def list_training_jobs_async( + self, + request: pai_studio_20220112_models.ListTrainingJobsRequest, + ) -> pai_studio_20220112_models.ListTrainingJobsResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.list_training_jobs_with_options_async(request, headers, runtime) + + def operate_node_with_options( + self, + node_id: str, + request: pai_studio_20220112_models.OperateNodeRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.OperateNodeResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.operation): + body['Operation'] = request.operation + if not UtilClient.is_unset(request.resource_group_id): + body['ResourceGroupId'] = request.resource_group_id + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='OperateNode', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/nodes/{OpenApiUtilClient.get_encode_param(node_id)}', + method='POST', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.OperateNodeResponse(), + self.call_api(params, req, runtime) + ) + + async def operate_node_with_options_async( + self, + node_id: str, + request: pai_studio_20220112_models.OperateNodeRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.OperateNodeResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.operation): + body['Operation'] = request.operation + if not UtilClient.is_unset(request.resource_group_id): + body['ResourceGroupId'] = request.resource_group_id + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='OperateNode', version='2022-01-12', protocol='HTTPS', - pathname=f'/api/v1/trainingjobs', - method='GET', + pathname=f'/api/v1/nodes/{OpenApiUtilClient.get_encode_param(node_id)}', + method='POST', auth_type='AK', style='ROA', req_body_type='json', body_type='json' ) return TeaCore.from_map( - pai_studio_20220112_models.ListTrainingJobsResponse(), + pai_studio_20220112_models.OperateNodeResponse(), await self.call_api_async(params, req, runtime) ) - def list_training_jobs( + def operate_node( self, - request: pai_studio_20220112_models.ListTrainingJobsRequest, - ) -> pai_studio_20220112_models.ListTrainingJobsResponse: + node_id: str, + request: pai_studio_20220112_models.OperateNodeRequest, + ) -> pai_studio_20220112_models.OperateNodeResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.list_training_jobs_with_options(request, headers, runtime) + return self.operate_node_with_options(node_id, request, headers, runtime) - async def list_training_jobs_async( + async def operate_node_async( self, - request: pai_studio_20220112_models.ListTrainingJobsRequest, - ) -> pai_studio_20220112_models.ListTrainingJobsResponse: + node_id: str, + request: pai_studio_20220112_models.OperateNodeRequest, + ) -> pai_studio_20220112_models.OperateNodeResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.list_training_jobs_with_options_async(request, headers, runtime) + return await self.operate_node_with_options_async(node_id, request, headers, runtime) def release_algorithm_with_options( self, @@ -6777,20 +9013,20 @@ async def scale_quota_async( headers = {} return await self.scale_quota_with_options_async(quota_id, request, headers, runtime) - def stop_arrears_training_job_with_options( + def stop_training_job_with_options( self, training_job_id: str, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.StopArrearsTrainingJobResponse: + ) -> pai_studio_20220112_models.StopTrainingJobResponse: req = open_api_models.OpenApiRequest( headers=headers ) params = open_api_models.Params( - action='StopArrearsTrainingJob', + action='StopTrainingJob', version='2022-01-12', protocol='HTTPS', - pathname=f'/api/v1/trainingjobs/{OpenApiUtilClient.get_encode_param(training_job_id)}/arrearage/stop', + pathname=f'/api/v1/trainingjobs/{OpenApiUtilClient.get_encode_param(training_job_id)}/stop', method='PUT', auth_type='AK', style='ROA', @@ -6798,24 +9034,24 @@ def stop_arrears_training_job_with_options( body_type='json' ) return TeaCore.from_map( - pai_studio_20220112_models.StopArrearsTrainingJobResponse(), + pai_studio_20220112_models.StopTrainingJobResponse(), self.call_api(params, req, runtime) ) - async def stop_arrears_training_job_with_options_async( + async def stop_training_job_with_options_async( self, training_job_id: str, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.StopArrearsTrainingJobResponse: + ) -> pai_studio_20220112_models.StopTrainingJobResponse: req = open_api_models.OpenApiRequest( headers=headers ) params = open_api_models.Params( - action='StopArrearsTrainingJob', + action='StopTrainingJob', version='2022-01-12', protocol='HTTPS', - pathname=f'/api/v1/trainingjobs/{OpenApiUtilClient.get_encode_param(training_job_id)}/arrearage/stop', + pathname=f'/api/v1/trainingjobs/{OpenApiUtilClient.get_encode_param(training_job_id)}/stop', method='PUT', auth_type='AK', style='ROA', @@ -6823,91 +9059,217 @@ async def stop_arrears_training_job_with_options_async( body_type='json' ) return TeaCore.from_map( - pai_studio_20220112_models.StopArrearsTrainingJobResponse(), + pai_studio_20220112_models.StopTrainingJobResponse(), await self.call_api_async(params, req, runtime) ) - def stop_arrears_training_job( + def stop_training_job( self, training_job_id: str, - ) -> pai_studio_20220112_models.StopArrearsTrainingJobResponse: + ) -> pai_studio_20220112_models.StopTrainingJobResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.stop_arrears_training_job_with_options(training_job_id, headers, runtime) + return self.stop_training_job_with_options(training_job_id, headers, runtime) - async def stop_arrears_training_job_async( + async def stop_training_job_async( self, training_job_id: str, - ) -> pai_studio_20220112_models.StopArrearsTrainingJobResponse: + ) -> pai_studio_20220112_models.StopTrainingJobResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.stop_arrears_training_job_with_options_async(training_job_id, headers, runtime) + return await self.stop_training_job_with_options_async(training_job_id, headers, runtime) - def stop_training_job_with_options( + def tag_resources_with_options( self, - training_job_id: str, + request: pai_studio_20220112_models.TagResourcesRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.StopTrainingJobResponse: + ) -> pai_studio_20220112_models.TagResourcesResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.region_id): + body['RegionId'] = request.region_id + if not UtilClient.is_unset(request.resource_id): + body['ResourceId'] = request.resource_id + if not UtilClient.is_unset(request.resource_type): + body['ResourceType'] = request.resource_type + if not UtilClient.is_unset(request.tag): + body['Tag'] = request.tag req = open_api_models.OpenApiRequest( - headers=headers + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) ) params = open_api_models.Params( - action='StopTrainingJob', + action='TagResources', version='2022-01-12', protocol='HTTPS', - pathname=f'/api/v1/trainingjobs/{OpenApiUtilClient.get_encode_param(training_job_id)}/stop', - method='PUT', + pathname=f'/api/v1/tags', + method='POST', auth_type='AK', style='ROA', req_body_type='json', body_type='json' ) return TeaCore.from_map( - pai_studio_20220112_models.StopTrainingJobResponse(), + pai_studio_20220112_models.TagResourcesResponse(), self.call_api(params, req, runtime) ) - async def stop_training_job_with_options_async( + async def tag_resources_with_options_async( self, - training_job_id: str, + request: pai_studio_20220112_models.TagResourcesRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.StopTrainingJobResponse: + ) -> pai_studio_20220112_models.TagResourcesResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.region_id): + body['RegionId'] = request.region_id + if not UtilClient.is_unset(request.resource_id): + body['ResourceId'] = request.resource_id + if not UtilClient.is_unset(request.resource_type): + body['ResourceType'] = request.resource_type + if not UtilClient.is_unset(request.tag): + body['Tag'] = request.tag req = open_api_models.OpenApiRequest( - headers=headers + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) ) params = open_api_models.Params( - action='StopTrainingJob', + action='TagResources', version='2022-01-12', protocol='HTTPS', - pathname=f'/api/v1/trainingjobs/{OpenApiUtilClient.get_encode_param(training_job_id)}/stop', - method='PUT', + pathname=f'/api/v1/tags', + method='POST', auth_type='AK', style='ROA', req_body_type='json', body_type='json' ) return TeaCore.from_map( - pai_studio_20220112_models.StopTrainingJobResponse(), + pai_studio_20220112_models.TagResourcesResponse(), await self.call_api_async(params, req, runtime) ) - def stop_training_job( + def tag_resources( self, - training_job_id: str, - ) -> pai_studio_20220112_models.StopTrainingJobResponse: + request: pai_studio_20220112_models.TagResourcesRequest, + ) -> pai_studio_20220112_models.TagResourcesResponse: runtime = util_models.RuntimeOptions() headers = {} - return self.stop_training_job_with_options(training_job_id, headers, runtime) + return self.tag_resources_with_options(request, headers, runtime) - async def stop_training_job_async( + async def tag_resources_async( self, - training_job_id: str, - ) -> pai_studio_20220112_models.StopTrainingJobResponse: + request: pai_studio_20220112_models.TagResourcesRequest, + ) -> pai_studio_20220112_models.TagResourcesResponse: runtime = util_models.RuntimeOptions() headers = {} - return await self.stop_training_job_with_options_async(training_job_id, headers, runtime) + return await self.tag_resources_with_options_async(request, headers, runtime) + + def untag_resources_with_options( + self, + tmp_req: pai_studio_20220112_models.UntagResourcesRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.UntagResourcesResponse: + UtilClient.validate_model(tmp_req) + request = pai_studio_20220112_models.UntagResourcesShrinkRequest() + OpenApiUtilClient.convert(tmp_req, request) + if not UtilClient.is_unset(tmp_req.resource_id): + request.resource_id_shrink = OpenApiUtilClient.array_to_string_with_specified_style(tmp_req.resource_id, 'ResourceId', 'json') + if not UtilClient.is_unset(tmp_req.tag_key): + request.tag_key_shrink = OpenApiUtilClient.array_to_string_with_specified_style(tmp_req.tag_key, 'TagKey', 'json') + query = {} + if not UtilClient.is_unset(request.all): + query['All'] = request.all + if not UtilClient.is_unset(request.region_id): + query['RegionId'] = request.region_id + if not UtilClient.is_unset(request.resource_id_shrink): + query['ResourceId'] = request.resource_id_shrink + if not UtilClient.is_unset(request.resource_type): + query['ResourceType'] = request.resource_type + if not UtilClient.is_unset(request.tag_key_shrink): + query['TagKey'] = request.tag_key_shrink + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='UntagResources', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/tags', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.UntagResourcesResponse(), + self.call_api(params, req, runtime) + ) + + async def untag_resources_with_options_async( + self, + tmp_req: pai_studio_20220112_models.UntagResourcesRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.UntagResourcesResponse: + UtilClient.validate_model(tmp_req) + request = pai_studio_20220112_models.UntagResourcesShrinkRequest() + OpenApiUtilClient.convert(tmp_req, request) + if not UtilClient.is_unset(tmp_req.resource_id): + request.resource_id_shrink = OpenApiUtilClient.array_to_string_with_specified_style(tmp_req.resource_id, 'ResourceId', 'json') + if not UtilClient.is_unset(tmp_req.tag_key): + request.tag_key_shrink = OpenApiUtilClient.array_to_string_with_specified_style(tmp_req.tag_key, 'TagKey', 'json') + query = {} + if not UtilClient.is_unset(request.all): + query['All'] = request.all + if not UtilClient.is_unset(request.region_id): + query['RegionId'] = request.region_id + if not UtilClient.is_unset(request.resource_id_shrink): + query['ResourceId'] = request.resource_id_shrink + if not UtilClient.is_unset(request.resource_type): + query['ResourceType'] = request.resource_type + if not UtilClient.is_unset(request.tag_key_shrink): + query['TagKey'] = request.tag_key_shrink + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='UntagResources', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/tags', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.UntagResourcesResponse(), + await self.call_api_async(params, req, runtime) + ) + + def untag_resources( + self, + request: pai_studio_20220112_models.UntagResourcesRequest, + ) -> pai_studio_20220112_models.UntagResourcesResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.untag_resources_with_options(request, headers, runtime) + + async def untag_resources_async( + self, + request: pai_studio_20220112_models.UntagResourcesRequest, + ) -> pai_studio_20220112_models.UntagResourcesResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.untag_resources_with_options_async(request, headers, runtime) def update_algorithm_with_options( self, @@ -7327,6 +9689,102 @@ async def update_component_version_snapshot_async( headers = {} return await self.update_component_version_snapshot_with_options_async(snapshot_id, headers, runtime) + def update_llmproject_with_options( + self, + project_id: str, + request: pai_studio_20220112_models.UpdateLLMProjectRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.UpdateLLMProjectResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.labels): + body['Labels'] = request.labels + if not UtilClient.is_unset(request.project_description): + body['ProjectDescription'] = request.project_description + if not UtilClient.is_unset(request.project_name): + body['ProjectName'] = request.project_name + if not UtilClient.is_unset(request.root_path): + body['RootPath'] = request.root_path + if not UtilClient.is_unset(request.runtime): + body['Runtime'] = request.runtime + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='UpdateLLMProject', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/langstudio/projects/{OpenApiUtilClient.get_encode_param(project_id)}', + method='PUT', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.UpdateLLMProjectResponse(), + self.call_api(params, req, runtime) + ) + + async def update_llmproject_with_options_async( + self, + project_id: str, + request: pai_studio_20220112_models.UpdateLLMProjectRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.UpdateLLMProjectResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.labels): + body['Labels'] = request.labels + if not UtilClient.is_unset(request.project_description): + body['ProjectDescription'] = request.project_description + if not UtilClient.is_unset(request.project_name): + body['ProjectName'] = request.project_name + if not UtilClient.is_unset(request.root_path): + body['RootPath'] = request.root_path + if not UtilClient.is_unset(request.runtime): + body['Runtime'] = request.runtime + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='UpdateLLMProject', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/langstudio/projects/{OpenApiUtilClient.get_encode_param(project_id)}', + method='PUT', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.UpdateLLMProjectResponse(), + await self.call_api_async(params, req, runtime) + ) + + def update_llmproject( + self, + project_id: str, + request: pai_studio_20220112_models.UpdateLLMProjectRequest, + ) -> pai_studio_20220112_models.UpdateLLMProjectResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.update_llmproject_with_options(project_id, request, headers, runtime) + + async def update_llmproject_async( + self, + project_id: str, + request: pai_studio_20220112_models.UpdateLLMProjectRequest, + ) -> pai_studio_20220112_models.UpdateLLMProjectResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.update_llmproject_with_options_async(project_id, request, headers, runtime) + def update_quota_with_options( self, quota_id: str, @@ -7340,6 +9798,8 @@ def update_quota_with_options( body['Description'] = request.description if not UtilClient.is_unset(request.labels): body['Labels'] = request.labels + if not UtilClient.is_unset(request.queue_strategy): + body['QueueStrategy'] = request.queue_strategy req = open_api_models.OpenApiRequest( headers=headers, body=OpenApiUtilClient.parse_to_map(body) @@ -7373,6 +9833,8 @@ async def update_quota_with_options_async( body['Description'] = request.description if not UtilClient.is_unset(request.labels): body['Labels'] = request.labels + if not UtilClient.is_unset(request.queue_strategy): + body['QueueStrategy'] = request.queue_strategy req = open_api_models.OpenApiRequest( headers=headers, body=OpenApiUtilClient.parse_to_map(body) @@ -7500,6 +9962,10 @@ def update_resource_group_with_options( ) -> pai_studio_20220112_models.UpdateResourceGroupResponse: UtilClient.validate_model(request) body = {} + if not UtilClient.is_unset(request.description): + body['Description'] = request.description + if not UtilClient.is_unset(request.name): + body['Name'] = request.name if not UtilClient.is_unset(request.unbind): body['Unbind'] = request.unbind if not UtilClient.is_unset(request.user_vpc): @@ -7533,6 +9999,10 @@ async def update_resource_group_with_options_async( ) -> pai_studio_20220112_models.UpdateResourceGroupResponse: UtilClient.validate_model(request) body = {} + if not UtilClient.is_unset(request.description): + body['Description'] = request.description + if not UtilClient.is_unset(request.name): + body['Name'] = request.name if not UtilClient.is_unset(request.unbind): body['Unbind'] = request.unbind if not UtilClient.is_unset(request.user_vpc): @@ -7575,6 +10045,90 @@ async def update_resource_group_async( headers = {} return await self.update_resource_group_with_options_async(resource_group_id, request, headers, runtime) + def update_resource_group_machine_group_with_options( + self, + resource_group_id: str, + machine_group_id: str, + request: pai_studio_20220112_models.UpdateResourceGroupMachineGroupRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.UpdateResourceGroupMachineGroupResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.name): + body['Name'] = request.name + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='UpdateResourceGroupMachineGroup', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/resources/{OpenApiUtilClient.get_encode_param(resource_group_id)}/machinegroups/{OpenApiUtilClient.get_encode_param(machine_group_id)}', + method='PUT', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.UpdateResourceGroupMachineGroupResponse(), + self.call_api(params, req, runtime) + ) + + async def update_resource_group_machine_group_with_options_async( + self, + resource_group_id: str, + machine_group_id: str, + request: pai_studio_20220112_models.UpdateResourceGroupMachineGroupRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.UpdateResourceGroupMachineGroupResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.name): + body['Name'] = request.name + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='UpdateResourceGroupMachineGroup', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/resources/{OpenApiUtilClient.get_encode_param(resource_group_id)}/machinegroups/{OpenApiUtilClient.get_encode_param(machine_group_id)}', + method='PUT', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.UpdateResourceGroupMachineGroupResponse(), + await self.call_api_async(params, req, runtime) + ) + + def update_resource_group_machine_group( + self, + resource_group_id: str, + machine_group_id: str, + request: pai_studio_20220112_models.UpdateResourceGroupMachineGroupRequest, + ) -> pai_studio_20220112_models.UpdateResourceGroupMachineGroupResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.update_resource_group_machine_group_with_options(resource_group_id, machine_group_id, request, headers, runtime) + + async def update_resource_group_machine_group_async( + self, + resource_group_id: str, + machine_group_id: str, + request: pai_studio_20220112_models.UpdateResourceGroupMachineGroupRequest, + ) -> pai_studio_20220112_models.UpdateResourceGroupMachineGroupResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.update_resource_group_machine_group_with_options_async(resource_group_id, machine_group_id, request, headers, runtime) + def update_training_job_labels_with_options( self, training_job_id: str, diff --git a/pai/libs/alibabacloud_paistudio20220112/models.py b/pai/libs/alibabacloud_paistudio20220112/models.py index 5092ef3..e5f6ecf 100644 --- a/pai/libs/alibabacloud_paistudio20220112/models.py +++ b/pai/libs/alibabacloud_paistudio20220112/models.py @@ -1,7 +1,40 @@ # -*- coding: utf-8 -*- # This file is auto-generated, don't edit it. Thanks. from Tea.model import TeaModel -from typing import Dict, Any, List +from typing import List, Dict, Any + + +class ACS(TeaModel): + def __init__( + self, + acsquota_id: str = None, + associated_products: List[str] = None, + ): + self.acsquota_id = acsquota_id + self.associated_products = associated_products + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.acsquota_id is not None: + result['ACSQuotaId'] = self.acsquota_id + if self.associated_products is not None: + result['AssociatedProducts'] = self.associated_products + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('ACSQuotaId') is not None: + self.acsquota_id = m.get('ACSQuotaId') + if m.get('AssociatedProducts') is not None: + self.associated_products = m.get('AssociatedProducts') + return self class AlgorithmSpecComputeResourcePolicy(TeaModel): @@ -1330,6 +1363,9 @@ def __init__( gmt_expired_time: str = None, gmt_modified_time: str = None, is_bound: bool = None, + limit_cpu: str = None, + limit_gpu: str = None, + limit_memory: str = None, machine_group_id: str = None, memory: str = None, node_name: str = None, @@ -1338,6 +1374,9 @@ def __init__( order_status: str = None, reason_code: str = None, reason_message: str = None, + request_cpu: str = None, + request_gpu: str = None, + request_memory: str = None, resource_group_id: str = None, resource_group_name: str = None, ): @@ -1351,6 +1390,9 @@ def __init__( self.gmt_expired_time = gmt_expired_time self.gmt_modified_time = gmt_modified_time self.is_bound = is_bound + self.limit_cpu = limit_cpu + self.limit_gpu = limit_gpu + self.limit_memory = limit_memory self.machine_group_id = machine_group_id self.memory = memory self.node_name = node_name @@ -1359,6 +1401,9 @@ def __init__( self.order_status = order_status self.reason_code = reason_code self.reason_message = reason_message + self.request_cpu = request_cpu + self.request_gpu = request_gpu + self.request_memory = request_memory self.resource_group_id = resource_group_id self.resource_group_name = resource_group_name @@ -1396,6 +1441,12 @@ def to_map(self): result['GmtModifiedTime'] = self.gmt_modified_time if self.is_bound is not None: result['IsBound'] = self.is_bound + if self.limit_cpu is not None: + result['LimitCPU'] = self.limit_cpu + if self.limit_gpu is not None: + result['LimitGPU'] = self.limit_gpu + if self.limit_memory is not None: + result['LimitMemory'] = self.limit_memory if self.machine_group_id is not None: result['MachineGroupId'] = self.machine_group_id if self.memory is not None: @@ -1412,6 +1463,12 @@ def to_map(self): result['ReasonCode'] = self.reason_code if self.reason_message is not None: result['ReasonMessage'] = self.reason_message + if self.request_cpu is not None: + result['RequestCPU'] = self.request_cpu + if self.request_gpu is not None: + result['RequestGPU'] = self.request_gpu + if self.request_memory is not None: + result['RequestMemory'] = self.request_memory if self.resource_group_id is not None: result['ResourceGroupId'] = self.resource_group_id if self.resource_group_name is not None: @@ -1443,6 +1500,12 @@ def from_map(self, m: dict = None): self.gmt_modified_time = m.get('GmtModifiedTime') if m.get('IsBound') is not None: self.is_bound = m.get('IsBound') + if m.get('LimitCPU') is not None: + self.limit_cpu = m.get('LimitCPU') + if m.get('LimitGPU') is not None: + self.limit_gpu = m.get('LimitGPU') + if m.get('LimitMemory') is not None: + self.limit_memory = m.get('LimitMemory') if m.get('MachineGroupId') is not None: self.machine_group_id = m.get('MachineGroupId') if m.get('Memory') is not None: @@ -1459,6 +1522,12 @@ def from_map(self, m: dict = None): self.reason_code = m.get('ReasonCode') if m.get('ReasonMessage') is not None: self.reason_message = m.get('ReasonMessage') + if m.get('RequestCPU') is not None: + self.request_cpu = m.get('RequestCPU') + if m.get('RequestGPU') is not None: + self.request_gpu = m.get('RequestGPU') + if m.get('RequestMemory') is not None: + self.request_memory = m.get('RequestMemory') if m.get('ResourceGroupId') is not None: self.resource_group_id = m.get('ResourceGroupId') if m.get('ResourceGroupName') is not None: @@ -1513,6 +1582,116 @@ def from_map(self, m: dict = None): return self +class ResourceAmount(TeaModel): + def __init__( + self, + cpu: str = None, + gpu: str = None, + gputype: str = None, + memory: str = None, + ): + self.cpu = cpu + self.gpu = gpu + self.gputype = gputype + self.memory = memory + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.cpu is not None: + result['CPU'] = self.cpu + if self.gpu is not None: + result['GPU'] = self.gpu + if self.gputype is not None: + result['GPUType'] = self.gputype + if self.memory is not None: + result['Memory'] = self.memory + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('CPU') is not None: + self.cpu = m.get('CPU') + if m.get('GPU') is not None: + self.gpu = m.get('GPU') + if m.get('GPUType') is not None: + self.gputype = m.get('GPUType') + if m.get('Memory') is not None: + self.memory = m.get('Memory') + return self + + +class NodePodInfo(TeaModel): + def __init__( + self, + phase: str = None, + pod_ip: str = None, + pod_name: str = None, + pod_namespace: str = None, + resource_spec: ResourceAmount = None, + workload_id: str = None, + workload_type: str = None, + ): + self.phase = phase + self.pod_ip = pod_ip + self.pod_name = pod_name + self.pod_namespace = pod_namespace + self.resource_spec = resource_spec + self.workload_id = workload_id + self.workload_type = workload_type + + def validate(self): + if self.resource_spec: + self.resource_spec.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.phase is not None: + result['Phase'] = self.phase + if self.pod_ip is not None: + result['PodIP'] = self.pod_ip + if self.pod_name is not None: + result['PodName'] = self.pod_name + if self.pod_namespace is not None: + result['PodNamespace'] = self.pod_namespace + if self.resource_spec is not None: + result['ResourceSpec'] = self.resource_spec.to_map() + if self.workload_id is not None: + result['WorkloadId'] = self.workload_id + if self.workload_type is not None: + result['WorkloadType'] = self.workload_type + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Phase') is not None: + self.phase = m.get('Phase') + if m.get('PodIP') is not None: + self.pod_ip = m.get('PodIP') + if m.get('PodName') is not None: + self.pod_name = m.get('PodName') + if m.get('PodNamespace') is not None: + self.pod_namespace = m.get('PodNamespace') + if m.get('ResourceSpec') is not None: + temp_model = ResourceAmount() + self.resource_spec = temp_model.from_map(m['ResourceSpec']) + if m.get('WorkloadId') is not None: + self.workload_id = m.get('WorkloadId') + if m.get('WorkloadType') is not None: + self.workload_type = m.get('WorkloadType') + return self + + class NodeType(TeaModel): def __init__( self, @@ -1795,6 +1974,284 @@ def from_map(self, m: dict = None): return self +class QueueInfo(TeaModel): + def __init__( + self, + code: str = None, + code_type: str = None, + gmt_dequeued_time: str = None, + gmt_enqueued_time: str = None, + gmt_position_modified_time: str = None, + name: str = None, + position: int = None, + priority: int = None, + queue_strategy: str = None, + quota_id: str = None, + reason: str = None, + resource: ResourceAmount = None, + status: str = None, + sub_status: str = None, + user_id: str = None, + workload_id: str = None, + workload_type: str = None, + workspace_id: str = None, + ): + self.code = code + self.code_type = code_type + self.gmt_dequeued_time = gmt_dequeued_time + self.gmt_enqueued_time = gmt_enqueued_time + self.gmt_position_modified_time = gmt_position_modified_time + self.name = name + self.position = position + self.priority = priority + self.queue_strategy = queue_strategy + self.quota_id = quota_id + self.reason = reason + self.resource = resource + self.status = status + self.sub_status = sub_status + self.user_id = user_id + self.workload_id = workload_id + self.workload_type = workload_type + self.workspace_id = workspace_id + + def validate(self): + if self.resource: + self.resource.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.code is not None: + result['Code'] = self.code + if self.code_type is not None: + result['CodeType'] = self.code_type + if self.gmt_dequeued_time is not None: + result['GmtDequeuedTime'] = self.gmt_dequeued_time + if self.gmt_enqueued_time is not None: + result['GmtEnqueuedTime'] = self.gmt_enqueued_time + if self.gmt_position_modified_time is not None: + result['GmtPositionModifiedTime'] = self.gmt_position_modified_time + if self.name is not None: + result['Name'] = self.name + if self.position is not None: + result['Position'] = self.position + if self.priority is not None: + result['Priority'] = self.priority + if self.queue_strategy is not None: + result['QueueStrategy'] = self.queue_strategy + if self.quota_id is not None: + result['QuotaId'] = self.quota_id + if self.reason is not None: + result['Reason'] = self.reason + if self.resource is not None: + result['Resource'] = self.resource.to_map() + if self.status is not None: + result['Status'] = self.status + if self.sub_status is not None: + result['SubStatus'] = self.sub_status + if self.user_id is not None: + result['UserId'] = self.user_id + if self.workload_id is not None: + result['WorkloadId'] = self.workload_id + if self.workload_type is not None: + result['WorkloadType'] = self.workload_type + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Code') is not None: + self.code = m.get('Code') + if m.get('CodeType') is not None: + self.code_type = m.get('CodeType') + if m.get('GmtDequeuedTime') is not None: + self.gmt_dequeued_time = m.get('GmtDequeuedTime') + if m.get('GmtEnqueuedTime') is not None: + self.gmt_enqueued_time = m.get('GmtEnqueuedTime') + if m.get('GmtPositionModifiedTime') is not None: + self.gmt_position_modified_time = m.get('GmtPositionModifiedTime') + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('Position') is not None: + self.position = m.get('Position') + if m.get('Priority') is not None: + self.priority = m.get('Priority') + if m.get('QueueStrategy') is not None: + self.queue_strategy = m.get('QueueStrategy') + if m.get('QuotaId') is not None: + self.quota_id = m.get('QuotaId') + if m.get('Reason') is not None: + self.reason = m.get('Reason') + if m.get('Resource') is not None: + temp_model = ResourceAmount() + self.resource = temp_model.from_map(m['Resource']) + if m.get('Status') is not None: + self.status = m.get('Status') + if m.get('SubStatus') is not None: + self.sub_status = m.get('SubStatus') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + if m.get('WorkloadId') is not None: + self.workload_id = m.get('WorkloadId') + if m.get('WorkloadType') is not None: + self.workload_type = m.get('WorkloadType') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') + return self + + +class ResourceSpec(TeaModel): + def __init__( + self, + node_specs: List[NodeSpec] = None, + ): + self.node_specs = node_specs + + def validate(self): + if self.node_specs: + for k in self.node_specs: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + result['NodeSpecs'] = [] + if self.node_specs is not None: + for k in self.node_specs: + result['NodeSpecs'].append(k.to_map() if k else None) + return result + + def from_map(self, m: dict = None): + m = m or dict() + self.node_specs = [] + if m.get('NodeSpecs') is not None: + for k in m.get('NodeSpecs'): + temp_model = NodeSpec() + self.node_specs.append(temp_model.from_map(k)) + return self + + +class WorkspaceSpec(TeaModel): + def __init__( + self, + code: str = None, + code_type: str = None, + is_guaranteed_valid: bool = None, + is_over_sold_valid: bool = None, + reason: str = None, + spec: ResourceAmount = None, + spec_name: str = None, + ): + self.code = code + self.code_type = code_type + self.is_guaranteed_valid = is_guaranteed_valid + self.is_over_sold_valid = is_over_sold_valid + self.reason = reason + self.spec = spec + self.spec_name = spec_name + + def validate(self): + if self.spec: + self.spec.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.code is not None: + result['Code'] = self.code + if self.code_type is not None: + result['CodeType'] = self.code_type + if self.is_guaranteed_valid is not None: + result['IsGuaranteedValid'] = self.is_guaranteed_valid + if self.is_over_sold_valid is not None: + result['IsOverSoldValid'] = self.is_over_sold_valid + if self.reason is not None: + result['Reason'] = self.reason + if self.spec is not None: + result['Spec'] = self.spec.to_map() + if self.spec_name is not None: + result['SpecName'] = self.spec_name + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Code') is not None: + self.code = m.get('Code') + if m.get('CodeType') is not None: + self.code_type = m.get('CodeType') + if m.get('IsGuaranteedValid') is not None: + self.is_guaranteed_valid = m.get('IsGuaranteedValid') + if m.get('IsOverSoldValid') is not None: + self.is_over_sold_valid = m.get('IsOverSoldValid') + if m.get('Reason') is not None: + self.reason = m.get('Reason') + if m.get('Spec') is not None: + temp_model = ResourceAmount() + self.spec = temp_model.from_map(m['Spec']) + if m.get('SpecName') is not None: + self.spec_name = m.get('SpecName') + return self + + +class WorkspaceSpecs(TeaModel): + def __init__( + self, + product: str = None, + specs: List[WorkspaceSpec] = None, + workspace_id: str = None, + ): + self.product = product + self.specs = specs + self.workspace_id = workspace_id + + def validate(self): + if self.specs: + for k in self.specs: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.product is not None: + result['Product'] = self.product + result['Specs'] = [] + if self.specs is not None: + for k in self.specs: + result['Specs'].append(k.to_map() if k else None) + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Product') is not None: + self.product = m.get('Product') + self.specs = [] + if m.get('Specs') is not None: + for k in m.get('Specs'): + temp_model = WorkspaceSpec() + self.specs.append(temp_model.from_map(k)) + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') + return self + + class UserVpc(TeaModel): def __init__( self, @@ -1855,19 +2312,29 @@ def from_map(self, m: dict = None): class QuotaConfig(TeaModel): def __init__( self, + acs: ACS = None, cluster_id: str = None, default_gpudriver: str = None, + resource_specs: List[WorkspaceSpecs] = None, support_gpudrivers: List[str] = None, support_rdma: bool = None, user_vpc: UserVpc = None, ): + self.acs = acs self.cluster_id = cluster_id self.default_gpudriver = default_gpudriver + self.resource_specs = resource_specs self.support_gpudrivers = support_gpudrivers self.support_rdma = support_rdma self.user_vpc = user_vpc def validate(self): + if self.acs: + self.acs.validate() + if self.resource_specs: + for k in self.resource_specs: + if k: + k.validate() if self.user_vpc: self.user_vpc.validate() @@ -1877,10 +2344,16 @@ def to_map(self): return _map result = dict() + if self.acs is not None: + result['ACS'] = self.acs.to_map() if self.cluster_id is not None: result['ClusterId'] = self.cluster_id if self.default_gpudriver is not None: result['DefaultGPUDriver'] = self.default_gpudriver + result['ResourceSpecs'] = [] + if self.resource_specs is not None: + for k in self.resource_specs: + result['ResourceSpecs'].append(k.to_map() if k else None) if self.support_gpudrivers is not None: result['SupportGPUDrivers'] = self.support_gpudrivers if self.support_rdma is not None: @@ -1891,10 +2364,18 @@ def to_map(self): def from_map(self, m: dict = None): m = m or dict() + if m.get('ACS') is not None: + temp_model = ACS() + self.acs = temp_model.from_map(m['ACS']) if m.get('ClusterId') is not None: self.cluster_id = m.get('ClusterId') if m.get('DefaultGPUDriver') is not None: self.default_gpudriver = m.get('DefaultGPUDriver') + self.resource_specs = [] + if m.get('ResourceSpecs') is not None: + for k in m.get('ResourceSpecs'): + temp_model = WorkspaceSpecs() + self.resource_specs.append(temp_model.from_map(k)) if m.get('SupportGPUDrivers') is not None: self.support_gpudrivers = m.get('SupportGPUDrivers') if m.get('SupportRDMA') is not None: @@ -1905,58 +2386,13 @@ def from_map(self, m: dict = None): return self -class ResourceAmount(TeaModel): +class QuotaDetails(TeaModel): def __init__( self, - cpu: str = None, - gpu: str = None, - gputype: str = None, - memory: str = None, - ): - self.cpu = cpu - self.gpu = gpu - self.gputype = gputype - self.memory = memory - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.cpu is not None: - result['CPU'] = self.cpu - if self.gpu is not None: - result['GPU'] = self.gpu - if self.gputype is not None: - result['GPUType'] = self.gputype - if self.memory is not None: - result['Memory'] = self.memory - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('CPU') is not None: - self.cpu = m.get('CPU') - if m.get('GPU') is not None: - self.gpu = m.get('GPU') - if m.get('GPUType') is not None: - self.gputype = m.get('GPUType') - if m.get('Memory') is not None: - self.memory = m.get('Memory') - return self - - -class QuotaDetails(TeaModel): - def __init__( - self, - actual_min_quota: ResourceAmount = None, - desired_min_quota: ResourceAmount = None, - requested_quota: ResourceAmount = None, - used_quota: ResourceAmount = None, + actual_min_quota: ResourceAmount = None, + desired_min_quota: ResourceAmount = None, + requested_quota: ResourceAmount = None, + used_quota: ResourceAmount = None, ): self.actual_min_quota = actual_min_quota self.desired_min_quota = desired_min_quota @@ -2043,8 +2479,9 @@ def __init__( gmt_modified_time: str = None, labels: List[Label] = None, latest_operation_id: str = None, - min: AllocateStrategySpec = None, + min: ResourceSpec = None, parent_quota_id: str = None, + queue_strategy: str = None, quota_config: QuotaConfig = None, quota_details: QuotaDetails = None, quota_id: str = None, @@ -2066,6 +2503,7 @@ def __init__( self.latest_operation_id = latest_operation_id self.min = min self.parent_quota_id = parent_quota_id + self.queue_strategy = queue_strategy self.quota_config = quota_config self.quota_details = quota_details self.quota_id = quota_id @@ -2124,6 +2562,8 @@ def to_map(self): result['Min'] = self.min.to_map() if self.parent_quota_id is not None: result['ParentQuotaId'] = self.parent_quota_id + if self.queue_strategy is not None: + result['QueueStrategy'] = self.queue_strategy if self.quota_config is not None: result['QuotaConfig'] = self.quota_config.to_map() if self.quota_details is not None: @@ -2172,10 +2612,12 @@ def from_map(self, m: dict = None): if m.get('LatestOperationId') is not None: self.latest_operation_id = m.get('LatestOperationId') if m.get('Min') is not None: - temp_model = AllocateStrategySpec() + temp_model = ResourceSpec() self.min = temp_model.from_map(m['Min']) if m.get('ParentQuotaId') is not None: self.parent_quota_id = m.get('ParentQuotaId') + if m.get('QueueStrategy') is not None: + self.queue_strategy = m.get('QueueStrategy') if m.get('QuotaConfig') is not None: temp_model = QuotaConfig() self.quota_config = temp_model.from_map(m['QuotaConfig']) @@ -2373,6 +2815,159 @@ def from_map(self, m: dict = None): return self +class QuotaNodeViewMetric(TeaModel): + def __init__( + self, + cpuusage_rate: str = None, + created_time: str = None, + disk_read_rate: str = None, + disk_write_rate: str = None, + gputype: str = None, + memory_usage_rate: str = None, + network_input_rate: str = None, + network_output_rate: str = None, + node_id: str = None, + node_status: str = None, + node_type: str = None, + quota_id: str = None, + request_cpu: int = None, + request_gpu: int = None, + request_memory: int = None, + task_id_map: Dict[str, Any] = None, + total_cpu: int = None, + total_gpu: int = None, + total_memory: int = None, + total_tasks: int = None, + user_ids: List[str] = None, + user_number: str = None, + ): + self.cpuusage_rate = cpuusage_rate + self.created_time = created_time + self.disk_read_rate = disk_read_rate + self.disk_write_rate = disk_write_rate + self.gputype = gputype + self.memory_usage_rate = memory_usage_rate + self.network_input_rate = network_input_rate + self.network_output_rate = network_output_rate + self.node_id = node_id + self.node_status = node_status + self.node_type = node_type + self.quota_id = quota_id + self.request_cpu = request_cpu + self.request_gpu = request_gpu + self.request_memory = request_memory + self.task_id_map = task_id_map + self.total_cpu = total_cpu + self.total_gpu = total_gpu + self.total_memory = total_memory + self.total_tasks = total_tasks + self.user_ids = user_ids + self.user_number = user_number + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.cpuusage_rate is not None: + result['CPUUsageRate'] = self.cpuusage_rate + if self.created_time is not None: + result['CreatedTime'] = self.created_time + if self.disk_read_rate is not None: + result['DiskReadRate'] = self.disk_read_rate + if self.disk_write_rate is not None: + result['DiskWriteRate'] = self.disk_write_rate + if self.gputype is not None: + result['GPUType'] = self.gputype + if self.memory_usage_rate is not None: + result['MemoryUsageRate'] = self.memory_usage_rate + if self.network_input_rate is not None: + result['NetworkInputRate'] = self.network_input_rate + if self.network_output_rate is not None: + result['NetworkOutputRate'] = self.network_output_rate + if self.node_id is not None: + result['NodeID'] = self.node_id + if self.node_status is not None: + result['NodeStatus'] = self.node_status + if self.node_type is not None: + result['NodeType'] = self.node_type + if self.quota_id is not None: + result['QuotaId'] = self.quota_id + if self.request_cpu is not None: + result['RequestCPU'] = self.request_cpu + if self.request_gpu is not None: + result['RequestGPU'] = self.request_gpu + if self.request_memory is not None: + result['RequestMemory'] = self.request_memory + if self.task_id_map is not None: + result['TaskIdMap'] = self.task_id_map + if self.total_cpu is not None: + result['TotalCPU'] = self.total_cpu + if self.total_gpu is not None: + result['TotalGPU'] = self.total_gpu + if self.total_memory is not None: + result['TotalMemory'] = self.total_memory + if self.total_tasks is not None: + result['TotalTasks'] = self.total_tasks + if self.user_ids is not None: + result['UserIDs'] = self.user_ids + if self.user_number is not None: + result['UserNumber'] = self.user_number + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('CPUUsageRate') is not None: + self.cpuusage_rate = m.get('CPUUsageRate') + if m.get('CreatedTime') is not None: + self.created_time = m.get('CreatedTime') + if m.get('DiskReadRate') is not None: + self.disk_read_rate = m.get('DiskReadRate') + if m.get('DiskWriteRate') is not None: + self.disk_write_rate = m.get('DiskWriteRate') + if m.get('GPUType') is not None: + self.gputype = m.get('GPUType') + if m.get('MemoryUsageRate') is not None: + self.memory_usage_rate = m.get('MemoryUsageRate') + if m.get('NetworkInputRate') is not None: + self.network_input_rate = m.get('NetworkInputRate') + if m.get('NetworkOutputRate') is not None: + self.network_output_rate = m.get('NetworkOutputRate') + if m.get('NodeID') is not None: + self.node_id = m.get('NodeID') + if m.get('NodeStatus') is not None: + self.node_status = m.get('NodeStatus') + if m.get('NodeType') is not None: + self.node_type = m.get('NodeType') + if m.get('QuotaId') is not None: + self.quota_id = m.get('QuotaId') + if m.get('RequestCPU') is not None: + self.request_cpu = m.get('RequestCPU') + if m.get('RequestGPU') is not None: + self.request_gpu = m.get('RequestGPU') + if m.get('RequestMemory') is not None: + self.request_memory = m.get('RequestMemory') + if m.get('TaskIdMap') is not None: + self.task_id_map = m.get('TaskIdMap') + if m.get('TotalCPU') is not None: + self.total_cpu = m.get('TotalCPU') + if m.get('TotalGPU') is not None: + self.total_gpu = m.get('TotalGPU') + if m.get('TotalMemory') is not None: + self.total_memory = m.get('TotalMemory') + if m.get('TotalTasks') is not None: + self.total_tasks = m.get('TotalTasks') + if m.get('UserIDs') is not None: + self.user_ids = m.get('UserIDs') + if m.get('UserNumber') is not None: + self.user_number = m.get('UserNumber') + return self + + class QuotaUserViewMetric(TeaModel): def __init__( self, @@ -2749,6 +3344,90 @@ def from_map(self, m: dict = None): return self +class SpotPriceItem(TeaModel): + def __init__( + self, + instance_type: str = None, + spot_discount: float = None, + timestamp: str = None, + zone_id: str = None, + ): + self.instance_type = instance_type + self.spot_discount = spot_discount + self.timestamp = timestamp + self.zone_id = zone_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.instance_type is not None: + result['InstanceType'] = self.instance_type + if self.spot_discount is not None: + result['SpotDiscount'] = self.spot_discount + if self.timestamp is not None: + result['Timestamp'] = self.timestamp + if self.zone_id is not None: + result['ZoneId'] = self.zone_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('InstanceType') is not None: + self.instance_type = m.get('InstanceType') + if m.get('SpotDiscount') is not None: + self.spot_discount = m.get('SpotDiscount') + if m.get('Timestamp') is not None: + self.timestamp = m.get('Timestamp') + if m.get('ZoneId') is not None: + self.zone_id = m.get('ZoneId') + return self + + +class SpotStockPreview(TeaModel): + def __init__( + self, + instance_type: str = None, + spot_discount: float = None, + stock_status: str = None, + ): + self.instance_type = instance_type + self.spot_discount = spot_discount + self.stock_status = stock_status + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.instance_type is not None: + result['InstanceType'] = self.instance_type + if self.spot_discount is not None: + result['SpotDiscount'] = self.spot_discount + if self.stock_status is not None: + result['StockStatus'] = self.stock_status + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('InstanceType') is not None: + self.instance_type = m.get('InstanceType') + if m.get('SpotDiscount') is not None: + self.spot_discount = m.get('SpotDiscount') + if m.get('StockStatus') is not None: + self.stock_status = m.get('StockStatus') + return self + + class UserViewMetric(TeaModel): def __init__( self, @@ -2908,18 +3587,16 @@ def from_map(self, m: dict = None): return self -class CreateAI4DDefaultBucketResponseBody(TeaModel): +class BuildLLMSnapshotRequestWorkloadContainer(TeaModel): def __init__( self, - extranet_endpoint: str = None, - intranet_endpoint: str = None, - name: str = None, - request_id: str = None, + image: str = None, + port: int = None, + user_command: str = None, ): - self.extranet_endpoint = extranet_endpoint - self.intranet_endpoint = intranet_endpoint - self.name = name - self.request_id = request_id + self.image = image + self.port = port + self.user_command = user_command def validate(self): pass @@ -2930,46 +3607,38 @@ def to_map(self): return _map result = dict() - if self.extranet_endpoint is not None: - result['ExtranetEndpoint'] = self.extranet_endpoint - if self.intranet_endpoint is not None: - result['IntranetEndpoint'] = self.intranet_endpoint - if self.name is not None: - result['Name'] = self.name - if self.request_id is not None: - result['RequestId'] = self.request_id + if self.image is not None: + result['Image'] = self.image + if self.port is not None: + result['Port'] = self.port + if self.user_command is not None: + result['UserCommand'] = self.user_command return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ExtranetEndpoint') is not None: - self.extranet_endpoint = m.get('ExtranetEndpoint') - if m.get('IntranetEndpoint') is not None: - self.intranet_endpoint = m.get('IntranetEndpoint') - if m.get('Name') is not None: - self.name = m.get('Name') - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') + if m.get('Image') is not None: + self.image = m.get('Image') + if m.get('Port') is not None: + self.port = m.get('Port') + if m.get('UserCommand') is not None: + self.user_command = m.get('UserCommand') return self -class CreateAI4DDefaultBucketResponse(TeaModel): +class BuildLLMSnapshotRequestWorkloadExtraConfig(TeaModel): def __init__( self, - headers: Dict[str, str] = None, - status_code: int = None, - body: CreateAI4DDefaultBucketResponseBody = None, + enable_webservice: bool = None, + job_max_running_time_minutes: int = None, + third_party_lib_dir: str = None, ): - self.headers = headers - self.status_code = status_code - self.body = body + self.enable_webservice = enable_webservice + self.job_max_running_time_minutes = job_max_running_time_minutes + self.third_party_lib_dir = third_party_lib_dir def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') - if self.body: - self.body.validate() + pass def to_map(self): _map = super().to_map() @@ -2977,36 +3646,37 @@ def to_map(self): return _map result = dict() - if self.headers is not None: - result['headers'] = self.headers - if self.status_code is not None: - result['statusCode'] = self.status_code - if self.body is not None: - result['body'] = self.body.to_map() + if self.enable_webservice is not None: + result['EnableWebservice'] = self.enable_webservice + if self.job_max_running_time_minutes is not None: + result['JobMaxRunningTimeMinutes'] = self.job_max_running_time_minutes + if self.third_party_lib_dir is not None: + result['ThirdPartyLibDir'] = self.third_party_lib_dir return result def from_map(self, m: dict = None): m = m or dict() - if m.get('headers') is not None: - self.headers = m.get('headers') - if m.get('statusCode') is not None: - self.status_code = m.get('statusCode') - if m.get('body') is not None: - temp_model = CreateAI4DDefaultBucketResponseBody() - self.body = temp_model.from_map(m['body']) + if m.get('EnableWebservice') is not None: + self.enable_webservice = m.get('EnableWebservice') + if m.get('JobMaxRunningTimeMinutes') is not None: + self.job_max_running_time_minutes = m.get('JobMaxRunningTimeMinutes') + if m.get('ThirdPartyLibDir') is not None: + self.third_party_lib_dir = m.get('ThirdPartyLibDir') return self -class CreateAI4DSerivceRequest(TeaModel): +class BuildLLMSnapshotRequestWorkloadResourceSpecResourceConfig(TeaModel): def __init__( self, - inference_spec: Dict[str, Any] = None, - service_type: str = None, - workspace_id: str = None, + cpu: int = None, + gpu: int = None, + memory_in_gi_b: int = None, + resource_group: str = None, ): - self.inference_spec = inference_spec - self.service_type = service_type - self.workspace_id = workspace_id + self.cpu = cpu + self.gpu = gpu + self.memory_in_gi_b = memory_in_gi_b + self.resource_group = resource_group def validate(self): pass @@ -3017,33 +3687,84 @@ def to_map(self): return _map result = dict() - if self.inference_spec is not None: - result['InferenceSpec'] = self.inference_spec - if self.service_type is not None: - result['ServiceType'] = self.service_type - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id + if self.cpu is not None: + result['Cpu'] = self.cpu + if self.gpu is not None: + result['Gpu'] = self.gpu + if self.memory_in_gi_b is not None: + result['MemoryInGiB'] = self.memory_in_gi_b + if self.resource_group is not None: + result['ResourceGroup'] = self.resource_group return result def from_map(self, m: dict = None): m = m or dict() - if m.get('InferenceSpec') is not None: - self.inference_spec = m.get('InferenceSpec') - if m.get('ServiceType') is not None: - self.service_type = m.get('ServiceType') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') + if m.get('Cpu') is not None: + self.cpu = m.get('Cpu') + if m.get('Gpu') is not None: + self.gpu = m.get('Gpu') + if m.get('MemoryInGiB') is not None: + self.memory_in_gi_b = m.get('MemoryInGiB') + if m.get('ResourceGroup') is not None: + self.resource_group = m.get('ResourceGroup') return self -class CreateAI4DSerivceResponseBody(TeaModel): +class BuildLLMSnapshotRequestWorkloadResourceSpec(TeaModel): def __init__( self, - request_id: str = None, - service_name: str = None, + ecs_spec: str = None, + instance_num: int = None, + resource_config: BuildLLMSnapshotRequestWorkloadResourceSpecResourceConfig = None, ): - self.request_id = request_id - self.service_name = service_name + self.ecs_spec = ecs_spec + self.instance_num = instance_num + self.resource_config = resource_config + + def validate(self): + if self.resource_config: + self.resource_config.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.ecs_spec is not None: + result['EcsSpec'] = self.ecs_spec + if self.instance_num is not None: + result['InstanceNum'] = self.instance_num + if self.resource_config is not None: + result['ResourceConfig'] = self.resource_config.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('EcsSpec') is not None: + self.ecs_spec = m.get('EcsSpec') + if m.get('InstanceNum') is not None: + self.instance_num = m.get('InstanceNum') + if m.get('ResourceConfig') is not None: + temp_model = BuildLLMSnapshotRequestWorkloadResourceSpecResourceConfig() + self.resource_config = temp_model.from_map(m['ResourceConfig']) + return self + + +class BuildLLMSnapshotRequestWorkloadUserVpc(TeaModel): + def __init__( + self, + default_route: str = None, + extended_cidrs: List[str] = None, + security_group_id: str = None, + switch_id: str = None, + vpc_id: str = None, + ): + self.default_route = default_route + self.extended_cidrs = extended_cidrs + self.security_group_id = security_group_id + self.switch_id = switch_id + self.vpc_id = vpc_id def validate(self): pass @@ -3054,38 +3775,55 @@ def to_map(self): return _map result = dict() - if self.request_id is not None: - result['RequestId'] = self.request_id - if self.service_name is not None: - result['ServiceName'] = self.service_name + if self.default_route is not None: + result['DefaultRoute'] = self.default_route + if self.extended_cidrs is not None: + result['ExtendedCIDRs'] = self.extended_cidrs + if self.security_group_id is not None: + result['SecurityGroupId'] = self.security_group_id + if self.switch_id is not None: + result['SwitchId'] = self.switch_id + if self.vpc_id is not None: + result['VpcId'] = self.vpc_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - if m.get('ServiceName') is not None: - self.service_name = m.get('ServiceName') + if m.get('DefaultRoute') is not None: + self.default_route = m.get('DefaultRoute') + if m.get('ExtendedCIDRs') is not None: + self.extended_cidrs = m.get('ExtendedCIDRs') + if m.get('SecurityGroupId') is not None: + self.security_group_id = m.get('SecurityGroupId') + if m.get('SwitchId') is not None: + self.switch_id = m.get('SwitchId') + if m.get('VpcId') is not None: + self.vpc_id = m.get('VpcId') return self -class CreateAI4DSerivceResponse(TeaModel): +class BuildLLMSnapshotRequestWorkload(TeaModel): def __init__( self, - headers: Dict[str, str] = None, - status_code: int = None, - body: CreateAI4DSerivceResponseBody = None, + container: BuildLLMSnapshotRequestWorkloadContainer = None, + extra_config: BuildLLMSnapshotRequestWorkloadExtraConfig = None, + resource_spec: BuildLLMSnapshotRequestWorkloadResourceSpec = None, + user_vpc: BuildLLMSnapshotRequestWorkloadUserVpc = None, ): - self.headers = headers - self.status_code = status_code - self.body = body + self.container = container + self.extra_config = extra_config + self.resource_spec = resource_spec + self.user_vpc = user_vpc def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') - if self.body: - self.body.validate() + if self.container: + self.container.validate() + if self.extra_config: + self.extra_config.validate() + if self.resource_spec: + self.resource_spec.validate() + if self.user_vpc: + self.user_vpc.validate() def to_map(self): _map = super().to_map() @@ -3093,41 +3831,49 @@ def to_map(self): return _map result = dict() - if self.headers is not None: - result['headers'] = self.headers - if self.status_code is not None: - result['statusCode'] = self.status_code - if self.body is not None: - result['body'] = self.body.to_map() + if self.container is not None: + result['Container'] = self.container.to_map() + if self.extra_config is not None: + result['ExtraConfig'] = self.extra_config.to_map() + if self.resource_spec is not None: + result['ResourceSpec'] = self.resource_spec.to_map() + if self.user_vpc is not None: + result['UserVpc'] = self.user_vpc.to_map() return result def from_map(self, m: dict = None): m = m or dict() - if m.get('headers') is not None: - self.headers = m.get('headers') - if m.get('statusCode') is not None: - self.status_code = m.get('statusCode') - if m.get('body') is not None: - temp_model = CreateAI4DSerivceResponseBody() - self.body = temp_model.from_map(m['body']) + if m.get('Container') is not None: + temp_model = BuildLLMSnapshotRequestWorkloadContainer() + self.container = temp_model.from_map(m['Container']) + if m.get('ExtraConfig') is not None: + temp_model = BuildLLMSnapshotRequestWorkloadExtraConfig() + self.extra_config = temp_model.from_map(m['ExtraConfig']) + if m.get('ResourceSpec') is not None: + temp_model = BuildLLMSnapshotRequestWorkloadResourceSpec() + self.resource_spec = temp_model.from_map(m['ResourceSpec']) + if m.get('UserVpc') is not None: + temp_model = BuildLLMSnapshotRequestWorkloadUserVpc() + self.user_vpc = temp_model.from_map(m['UserVpc']) return self -class CreateAlgorithmRequest(TeaModel): +class BuildLLMSnapshotRequest(TeaModel): def __init__( self, - algorithm_description: str = None, - algorithm_name: str = None, + description: str = None, display_name: str = None, - workspace_id: str = None, + labels: Dict[str, Any] = None, + workload: BuildLLMSnapshotRequestWorkload = None, ): - self.algorithm_description = algorithm_description - self.algorithm_name = algorithm_name + self.description = description self.display_name = display_name - self.workspace_id = workspace_id + self.labels = labels + self.workload = workload def validate(self): - pass + if self.workload: + self.workload.validate() def to_map(self): _map = super().to_map() @@ -3135,37 +3881,44 @@ def to_map(self): return _map result = dict() - if self.algorithm_description is not None: - result['AlgorithmDescription'] = self.algorithm_description - if self.algorithm_name is not None: - result['AlgorithmName'] = self.algorithm_name + if self.description is not None: + result['Description'] = self.description if self.display_name is not None: result['DisplayName'] = self.display_name - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id + if self.labels is not None: + result['Labels'] = self.labels + if self.workload is not None: + result['Workload'] = self.workload.to_map() return result def from_map(self, m: dict = None): m = m or dict() - if m.get('AlgorithmDescription') is not None: - self.algorithm_description = m.get('AlgorithmDescription') - if m.get('AlgorithmName') is not None: - self.algorithm_name = m.get('AlgorithmName') + if m.get('Description') is not None: + self.description = m.get('Description') if m.get('DisplayName') is not None: self.display_name = m.get('DisplayName') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') + if m.get('Labels') is not None: + self.labels = m.get('Labels') + if m.get('Workload') is not None: + temp_model = BuildLLMSnapshotRequestWorkload() + self.workload = temp_model.from_map(m['Workload']) return self -class CreateAlgorithmResponseBody(TeaModel): +class BuildLLMSnapshotResponseBody(TeaModel): def __init__( self, - algorithm_id: str = None, + job_id: str = None, + job_name: str = None, + job_request_id: str = None, request_id: str = None, + status: str = None, ): - self.algorithm_id = algorithm_id + self.job_id = job_id + self.job_name = job_name + self.job_request_id = job_request_id self.request_id = request_id + self.status = status def validate(self): pass @@ -3176,36 +3929,45 @@ def to_map(self): return _map result = dict() - if self.algorithm_id is not None: - result['AlgorithmId'] = self.algorithm_id + if self.job_id is not None: + result['JobId'] = self.job_id + if self.job_name is not None: + result['JobName'] = self.job_name + if self.job_request_id is not None: + result['JobRequestId'] = self.job_request_id if self.request_id is not None: result['RequestId'] = self.request_id + if self.status is not None: + result['Status'] = self.status return result def from_map(self, m: dict = None): m = m or dict() - if m.get('AlgorithmId') is not None: - self.algorithm_id = m.get('AlgorithmId') + if m.get('JobId') is not None: + self.job_id = m.get('JobId') + if m.get('JobName') is not None: + self.job_name = m.get('JobName') + if m.get('JobRequestId') is not None: + self.job_request_id = m.get('JobRequestId') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') + if m.get('Status') is not None: + self.status = m.get('Status') return self -class CreateAlgorithmResponse(TeaModel): +class BuildLLMSnapshotResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: CreateAlgorithmResponseBody = None, + body: BuildLLMSnapshotResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -3230,21 +3992,20 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = CreateAlgorithmResponseBody() + temp_model = BuildLLMSnapshotResponseBody() self.body = temp_model.from_map(m['body']) return self -class CreateAlgorithmVersionRequest(TeaModel): +class CheckInstanceWebTerminalRequest(TeaModel): def __init__( self, - algorithm_spec: AlgorithmSpec = None, + check_info: str = None, ): - self.algorithm_spec = algorithm_spec + self.check_info = check_info def validate(self): - if self.algorithm_spec: - self.algorithm_spec.validate() + pass def to_map(self): _map = super().to_map() @@ -3252,24 +4013,23 @@ def to_map(self): return _map result = dict() - if self.algorithm_spec is not None: - result['AlgorithmSpec'] = self.algorithm_spec.to_map() + if self.check_info is not None: + result['CheckInfo'] = self.check_info return result def from_map(self, m: dict = None): m = m or dict() - if m.get('AlgorithmSpec') is not None: - temp_model = AlgorithmSpec() - self.algorithm_spec = temp_model.from_map(m['AlgorithmSpec']) + if m.get('CheckInfo') is not None: + self.check_info = m.get('CheckInfo') return self -class CreateAlgorithmVersionShrinkRequest(TeaModel): +class CheckInstanceWebTerminalResponseBody(TeaModel): def __init__( self, - algorithm_spec_shrink: str = None, + request_id: str = None, ): - self.algorithm_spec_shrink = algorithm_spec_shrink + self.request_id = request_id def validate(self): pass @@ -3280,28 +4040,31 @@ def to_map(self): return _map result = dict() - if self.algorithm_spec_shrink is not None: - result['AlgorithmSpec'] = self.algorithm_spec_shrink + if self.request_id is not None: + result['RequestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('AlgorithmSpec') is not None: - self.algorithm_spec_shrink = m.get('AlgorithmSpec') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') return self -class CreateAlgorithmVersionResponseBody(TeaModel): +class CheckInstanceWebTerminalResponse(TeaModel): def __init__( self, - algorithm_id: str = None, - algorithm_version: str = None, + headers: Dict[str, str] = None, + status_code: int = None, + body: CheckInstanceWebTerminalResponseBody = None, ): - self.algorithm_id = algorithm_id - self.algorithm_version = algorithm_version + self.headers = headers + self.status_code = status_code + self.body = body def validate(self): - pass + if self.body: + self.body.validate() def to_map(self): _map = super().to_map() @@ -3309,36 +4072,83 @@ def to_map(self): return _map result = dict() - if self.algorithm_id is not None: - result['AlgorithmId'] = self.algorithm_id - if self.algorithm_version is not None: - result['AlgorithmVersion'] = self.algorithm_version + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() return result def from_map(self, m: dict = None): m = m or dict() - if m.get('AlgorithmId') is not None: - self.algorithm_id = m.get('AlgorithmId') - if m.get('AlgorithmVersion') is not None: - self.algorithm_version = m.get('AlgorithmVersion') + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = CheckInstanceWebTerminalResponseBody() + self.body = temp_model.from_map(m['body']) return self -class CreateAlgorithmVersionResponse(TeaModel): +class CreateAI4DDefaultBucketResponseBody(TeaModel): + def __init__( + self, + extranet_endpoint: str = None, + intranet_endpoint: str = None, + name: str = None, + request_id: str = None, + ): + self.extranet_endpoint = extranet_endpoint + self.intranet_endpoint = intranet_endpoint + self.name = name + self.request_id = request_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.extranet_endpoint is not None: + result['ExtranetEndpoint'] = self.extranet_endpoint + if self.intranet_endpoint is not None: + result['IntranetEndpoint'] = self.intranet_endpoint + if self.name is not None: + result['Name'] = self.name + if self.request_id is not None: + result['RequestId'] = self.request_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('ExtranetEndpoint') is not None: + self.extranet_endpoint = m.get('ExtranetEndpoint') + if m.get('IntranetEndpoint') is not None: + self.intranet_endpoint = m.get('IntranetEndpoint') + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + return self + + +class CreateAI4DDefaultBucketResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: CreateAlgorithmVersionResponseBody = None, + body: CreateAI4DDefaultBucketResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -3363,31 +4173,24 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = CreateAlgorithmVersionResponseBody() + temp_model = CreateAI4DDefaultBucketResponseBody() self.body = temp_model.from_map(m['body']) return self -class CreateComponentRequest(TeaModel): +class CreateAI4DSerivceRequest(TeaModel): def __init__( self, - description: str = None, - display_name: str = None, - labels: List[Label] = None, - name: str = None, + inference_spec: Dict[str, Any] = None, + service_type: str = None, workspace_id: str = None, ): - self.description = description - self.display_name = display_name - self.labels = labels - self.name = name + self.inference_spec = inference_spec + self.service_type = service_type self.workspace_id = workspace_id def validate(self): - if self.labels: - for k in self.labels: - if k: - k.validate() + pass def to_map(self): _map = super().to_map() @@ -3395,46 +4198,33 @@ def to_map(self): return _map result = dict() - if self.description is not None: - result['Description'] = self.description - if self.display_name is not None: - result['DisplayName'] = self.display_name - result['Labels'] = [] - if self.labels is not None: - for k in self.labels: - result['Labels'].append(k.to_map() if k else None) - if self.name is not None: - result['Name'] = self.name + if self.inference_spec is not None: + result['InferenceSpec'] = self.inference_spec + if self.service_type is not None: + result['ServiceType'] = self.service_type if self.workspace_id is not None: result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Description') is not None: - self.description = m.get('Description') - if m.get('DisplayName') is not None: - self.display_name = m.get('DisplayName') - self.labels = [] - if m.get('Labels') is not None: - for k in m.get('Labels'): - temp_model = Label() - self.labels.append(temp_model.from_map(k)) - if m.get('Name') is not None: - self.name = m.get('Name') + if m.get('InferenceSpec') is not None: + self.inference_spec = m.get('InferenceSpec') + if m.get('ServiceType') is not None: + self.service_type = m.get('ServiceType') if m.get('WorkspaceId') is not None: self.workspace_id = m.get('WorkspaceId') return self -class CreateComponentResponseBody(TeaModel): +class CreateAI4DSerivceResponseBody(TeaModel): def __init__( self, - component_id: str = None, request_id: str = None, + service_name: str = None, ): - self.component_id = component_id self.request_id = request_id + self.service_name = service_name def validate(self): pass @@ -3445,36 +4235,33 @@ def to_map(self): return _map result = dict() - if self.component_id is not None: - result['ComponentId'] = self.component_id if self.request_id is not None: result['RequestId'] = self.request_id + if self.service_name is not None: + result['ServiceName'] = self.service_name return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ComponentId') is not None: - self.component_id = m.get('ComponentId') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') + if m.get('ServiceName') is not None: + self.service_name = m.get('ServiceName') return self -class CreateComponentResponse(TeaModel): +class CreateAI4DSerivceResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: CreateComponentResponseBody = None, + body: CreateAI4DSerivceResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -3499,19 +4286,23 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = CreateComponentResponseBody() + temp_model = CreateAI4DSerivceResponseBody() self.body = temp_model.from_map(m['body']) return self -class CreateComponentVersionRequestLabels(TeaModel): +class CreateAlgorithmRequest(TeaModel): def __init__( self, - key: str = None, - value: str = None, + algorithm_description: str = None, + algorithm_name: str = None, + display_name: str = None, + workspace_id: str = None, ): - self.key = key - self.value = value + self.algorithm_description = algorithm_description + self.algorithm_name = algorithm_name + self.display_name = display_name + self.workspace_id = workspace_id def validate(self): pass @@ -3522,93 +4313,36 @@ def to_map(self): return _map result = dict() - if self.key is not None: - result['Key'] = self.key - if self.value is not None: - result['Value'] = self.value - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('Key') is not None: - self.key = m.get('Key') - if m.get('Value') is not None: - self.value = m.get('Value') - return self - - -class CreateComponentVersionRequest(TeaModel): - def __init__( - self, - config_dir: Location = None, - description: str = None, - labels: List[CreateComponentVersionRequestLabels] = None, - spec: ComponentSpec = None, - version: str = None, - ): - self.config_dir = config_dir - self.description = description - self.labels = labels - self.spec = spec - self.version = version - - def validate(self): - if self.config_dir: - self.config_dir.validate() - if self.labels: - for k in self.labels: - if k: - k.validate() - if self.spec: - self.spec.validate() - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.config_dir is not None: - result['ConfigDir'] = self.config_dir.to_map() - if self.description is not None: - result['Description'] = self.description - result['Labels'] = [] - if self.labels is not None: - for k in self.labels: - result['Labels'].append(k.to_map() if k else None) - if self.spec is not None: - result['Spec'] = self.spec.to_map() - if self.version is not None: - result['Version'] = self.version + if self.algorithm_description is not None: + result['AlgorithmDescription'] = self.algorithm_description + if self.algorithm_name is not None: + result['AlgorithmName'] = self.algorithm_name + if self.display_name is not None: + result['DisplayName'] = self.display_name + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ConfigDir') is not None: - temp_model = Location() - self.config_dir = temp_model.from_map(m['ConfigDir']) - if m.get('Description') is not None: - self.description = m.get('Description') - self.labels = [] - if m.get('Labels') is not None: - for k in m.get('Labels'): - temp_model = CreateComponentVersionRequestLabels() - self.labels.append(temp_model.from_map(k)) - if m.get('Spec') is not None: - temp_model = ComponentSpec() - self.spec = temp_model.from_map(m['Spec']) - if m.get('Version') is not None: - self.version = m.get('Version') + if m.get('AlgorithmDescription') is not None: + self.algorithm_description = m.get('AlgorithmDescription') + if m.get('AlgorithmName') is not None: + self.algorithm_name = m.get('AlgorithmName') + if m.get('DisplayName') is not None: + self.display_name = m.get('DisplayName') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class CreateComponentVersionResponseBody(TeaModel): +class CreateAlgorithmResponseBody(TeaModel): def __init__( self, - instance_job_id: str = None, + algorithm_id: str = None, request_id: str = None, ): - self.instance_job_id = instance_job_id + self.algorithm_id = algorithm_id self.request_id = request_id def validate(self): @@ -3620,36 +4354,33 @@ def to_map(self): return _map result = dict() - if self.instance_job_id is not None: - result['InstanceJobId'] = self.instance_job_id + if self.algorithm_id is not None: + result['AlgorithmId'] = self.algorithm_id if self.request_id is not None: result['RequestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('InstanceJobId') is not None: - self.instance_job_id = m.get('InstanceJobId') + if m.get('AlgorithmId') is not None: + self.algorithm_id = m.get('AlgorithmId') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') return self -class CreateComponentVersionResponse(TeaModel): +class CreateAlgorithmResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: CreateComponentVersionResponseBody = None, + body: CreateAlgorithmResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -3674,39 +4405,21 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = CreateComponentVersionResponseBody() + temp_model = CreateAlgorithmResponseBody() self.body = temp_model.from_map(m['body']) return self -class CreateQuotaRequest(TeaModel): +class CreateAlgorithmVersionRequest(TeaModel): def __init__( self, - allocate_strategy: str = None, - description: str = None, - labels: List[Label] = None, - min: AllocateStrategySpec = None, - parent_quota_id: str = None, - quota_name: str = None, - resource_group_ids: List[str] = None, - resource_type: str = None, + algorithm_spec: AlgorithmSpec = None, ): - self.allocate_strategy = allocate_strategy - self.description = description - self.labels = labels - self.min = min - self.parent_quota_id = parent_quota_id - self.quota_name = quota_name - self.resource_group_ids = resource_group_ids - self.resource_type = resource_type + self.algorithm_spec = algorithm_spec def validate(self): - if self.labels: - for k in self.labels: - if k: - k.validate() - if self.min: - self.min.validate() + if self.algorithm_spec: + self.algorithm_spec.validate() def to_map(self): _map = super().to_map() @@ -3714,59 +4427,24 @@ def to_map(self): return _map result = dict() - if self.allocate_strategy is not None: - result['AllocateStrategy'] = self.allocate_strategy - if self.description is not None: - result['Description'] = self.description - result['Labels'] = [] - if self.labels is not None: - for k in self.labels: - result['Labels'].append(k.to_map() if k else None) - if self.min is not None: - result['Min'] = self.min.to_map() - if self.parent_quota_id is not None: - result['ParentQuotaId'] = self.parent_quota_id - if self.quota_name is not None: - result['QuotaName'] = self.quota_name - if self.resource_group_ids is not None: - result['ResourceGroupIds'] = self.resource_group_ids - if self.resource_type is not None: - result['ResourceType'] = self.resource_type + if self.algorithm_spec is not None: + result['AlgorithmSpec'] = self.algorithm_spec.to_map() return result def from_map(self, m: dict = None): m = m or dict() - if m.get('AllocateStrategy') is not None: - self.allocate_strategy = m.get('AllocateStrategy') - if m.get('Description') is not None: - self.description = m.get('Description') - self.labels = [] - if m.get('Labels') is not None: - for k in m.get('Labels'): - temp_model = Label() - self.labels.append(temp_model.from_map(k)) - if m.get('Min') is not None: - temp_model = AllocateStrategySpec() - self.min = temp_model.from_map(m['Min']) - if m.get('ParentQuotaId') is not None: - self.parent_quota_id = m.get('ParentQuotaId') - if m.get('QuotaName') is not None: - self.quota_name = m.get('QuotaName') - if m.get('ResourceGroupIds') is not None: - self.resource_group_ids = m.get('ResourceGroupIds') - if m.get('ResourceType') is not None: - self.resource_type = m.get('ResourceType') + if m.get('AlgorithmSpec') is not None: + temp_model = AlgorithmSpec() + self.algorithm_spec = temp_model.from_map(m['AlgorithmSpec']) return self -class CreateQuotaResponseBody(TeaModel): +class CreateAlgorithmVersionShrinkRequest(TeaModel): def __init__( self, - quota_id: str = None, - request_id: str = None, + algorithm_spec_shrink: str = None, ): - self.quota_id = quota_id - self.request_id = request_id + self.algorithm_spec_shrink = algorithm_spec_shrink def validate(self): pass @@ -3777,36 +4455,62 @@ def to_map(self): return _map result = dict() - if self.quota_id is not None: - result['QuotaId'] = self.quota_id - if self.request_id is not None: - result['RequestId'] = self.request_id + if self.algorithm_spec_shrink is not None: + result['AlgorithmSpec'] = self.algorithm_spec_shrink return result def from_map(self, m: dict = None): m = m or dict() - if m.get('QuotaId') is not None: - self.quota_id = m.get('QuotaId') - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') + if m.get('AlgorithmSpec') is not None: + self.algorithm_spec_shrink = m.get('AlgorithmSpec') return self -class CreateQuotaResponse(TeaModel): +class CreateAlgorithmVersionResponseBody(TeaModel): + def __init__( + self, + algorithm_id: str = None, + algorithm_version: str = None, + ): + self.algorithm_id = algorithm_id + self.algorithm_version = algorithm_version + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.algorithm_id is not None: + result['AlgorithmId'] = self.algorithm_id + if self.algorithm_version is not None: + result['AlgorithmVersion'] = self.algorithm_version + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('AlgorithmId') is not None: + self.algorithm_id = m.get('AlgorithmId') + if m.get('AlgorithmVersion') is not None: + self.algorithm_version = m.get('AlgorithmVersion') + return self + + +class CreateAlgorithmVersionResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: CreateQuotaResponseBody = None, + body: CreateAlgorithmVersionResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -3831,29 +4535,31 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = CreateQuotaResponseBody() + temp_model = CreateAlgorithmVersionResponseBody() self.body = temp_model.from_map(m['body']) return self -class CreateResourceGroupRequest(TeaModel): +class CreateComponentRequest(TeaModel): def __init__( self, - computing_resource_provider: str = None, description: str = None, + display_name: str = None, + labels: List[Label] = None, name: str = None, - resource_type: str = None, - user_vpc: UserVpc = None, + workspace_id: str = None, ): - self.computing_resource_provider = computing_resource_provider self.description = description + self.display_name = display_name + self.labels = labels self.name = name - self.resource_type = resource_type - self.user_vpc = user_vpc + self.workspace_id = workspace_id def validate(self): - if self.user_vpc: - self.user_vpc.validate() + if self.labels: + for k in self.labels: + if k: + k.validate() def to_map(self): _map = super().to_map() @@ -3861,42 +4567,46 @@ def to_map(self): return _map result = dict() - if self.computing_resource_provider is not None: - result['ComputingResourceProvider'] = self.computing_resource_provider if self.description is not None: result['Description'] = self.description + if self.display_name is not None: + result['DisplayName'] = self.display_name + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) if self.name is not None: result['Name'] = self.name - if self.resource_type is not None: - result['ResourceType'] = self.resource_type - if self.user_vpc is not None: - result['UserVpc'] = self.user_vpc.to_map() + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ComputingResourceProvider') is not None: - self.computing_resource_provider = m.get('ComputingResourceProvider') if m.get('Description') is not None: self.description = m.get('Description') + if m.get('DisplayName') is not None: + self.display_name = m.get('DisplayName') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = Label() + self.labels.append(temp_model.from_map(k)) if m.get('Name') is not None: self.name = m.get('Name') - if m.get('ResourceType') is not None: - self.resource_type = m.get('ResourceType') - if m.get('UserVpc') is not None: - temp_model = UserVpc() - self.user_vpc = temp_model.from_map(m['UserVpc']) + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class CreateResourceGroupResponseBody(TeaModel): +class CreateComponentResponseBody(TeaModel): def __init__( self, + component_id: str = None, request_id: str = None, - resource_group_id: str = None, ): + self.component_id = component_id self.request_id = request_id - self.resource_group_id = resource_group_id def validate(self): pass @@ -3907,36 +4617,33 @@ def to_map(self): return _map result = dict() + if self.component_id is not None: + result['ComponentId'] = self.component_id if self.request_id is not None: result['RequestId'] = self.request_id - if self.resource_group_id is not None: - result['ResourceGroupID'] = self.resource_group_id return result def from_map(self, m: dict = None): m = m or dict() + if m.get('ComponentId') is not None: + self.component_id = m.get('ComponentId') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('ResourceGroupID') is not None: - self.resource_group_id = m.get('ResourceGroupID') return self -class CreateResourceGroupResponse(TeaModel): +class CreateComponentResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: CreateResourceGroupResponseBody = None, + body: CreateComponentResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -3961,17 +4668,19 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = CreateResourceGroupResponseBody() + temp_model = CreateComponentResponseBody() self.body = temp_model.from_map(m['body']) return self -class CreateServiceIdentityRoleRequest(TeaModel): +class CreateComponentVersionRequestLabels(TeaModel): def __init__( self, - role_name: str = None, + key: str = None, + value: str = None, ): - self.role_name = role_name + self.key = key + self.value = value def validate(self): pass @@ -3982,28 +4691,45 @@ def to_map(self): return _map result = dict() - if self.role_name is not None: - result['RoleName'] = self.role_name + if self.key is not None: + result['Key'] = self.key + if self.value is not None: + result['Value'] = self.value return result def from_map(self, m: dict = None): m = m or dict() - if m.get('RoleName') is not None: - self.role_name = m.get('RoleName') + if m.get('Key') is not None: + self.key = m.get('Key') + if m.get('Value') is not None: + self.value = m.get('Value') return self -class CreateServiceIdentityRoleResponseBody(TeaModel): +class CreateComponentVersionRequest(TeaModel): def __init__( self, - request_id: str = None, - role_name: str = None, + config_dir: Location = None, + description: str = None, + labels: List[CreateComponentVersionRequestLabels] = None, + spec: ComponentSpec = None, + version: str = None, ): - self.request_id = request_id - self.role_name = role_name + self.config_dir = config_dir + self.description = description + self.labels = labels + self.spec = spec + self.version = version def validate(self): - pass + if self.config_dir: + self.config_dir.validate() + if self.labels: + for k in self.labels: + if k: + k.validate() + if self.spec: + self.spec.validate() def to_map(self): _map = super().to_map() @@ -4011,36 +4737,85 @@ def to_map(self): return _map result = dict() - if self.request_id is not None: - result['RequestId'] = self.request_id - if self.role_name is not None: - result['RoleName'] = self.role_name - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - if m.get('RoleName') is not None: - self.role_name = m.get('RoleName') + if self.config_dir is not None: + result['ConfigDir'] = self.config_dir.to_map() + if self.description is not None: + result['Description'] = self.description + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + if self.spec is not None: + result['Spec'] = self.spec.to_map() + if self.version is not None: + result['Version'] = self.version + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('ConfigDir') is not None: + temp_model = Location() + self.config_dir = temp_model.from_map(m['ConfigDir']) + if m.get('Description') is not None: + self.description = m.get('Description') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = CreateComponentVersionRequestLabels() + self.labels.append(temp_model.from_map(k)) + if m.get('Spec') is not None: + temp_model = ComponentSpec() + self.spec = temp_model.from_map(m['Spec']) + if m.get('Version') is not None: + self.version = m.get('Version') return self -class CreateServiceIdentityRoleResponse(TeaModel): +class CreateComponentVersionResponseBody(TeaModel): + def __init__( + self, + instance_job_id: str = None, + request_id: str = None, + ): + self.instance_job_id = instance_job_id + self.request_id = request_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.instance_job_id is not None: + result['InstanceJobId'] = self.instance_job_id + if self.request_id is not None: + result['RequestId'] = self.request_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('InstanceJobId') is not None: + self.instance_job_id = m.get('InstanceJobId') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + return self + + +class CreateComponentVersionResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: CreateServiceIdentityRoleResponseBody = None, + body: CreateComponentVersionResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -4065,25 +4840,19 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = CreateServiceIdentityRoleResponseBody() + temp_model = CreateComponentVersionResponseBody() self.body = temp_model.from_map(m['body']) return self -class CreateTrainingJobRequestComputeResourceInstanceSpec(TeaModel): +class CreateInstanceWebTerminalResponseBody(TeaModel): def __init__( self, - cpu: str = None, - gpu: str = None, - gputype: str = None, - memory: str = None, - shared_memory: str = None, + request_id: str = None, + web_terminal_id: str = None, ): - self.cpu = cpu - self.gpu = gpu - self.gputype = gputype - self.memory = memory - self.shared_memory = shared_memory + self.request_id = request_id + self.web_terminal_id = web_terminal_id def validate(self): pass @@ -4094,51 +4863,35 @@ def to_map(self): return _map result = dict() - if self.cpu is not None: - result['CPU'] = self.cpu - if self.gpu is not None: - result['GPU'] = self.gpu - if self.gputype is not None: - result['GPUType'] = self.gputype - if self.memory is not None: - result['Memory'] = self.memory - if self.shared_memory is not None: - result['SharedMemory'] = self.shared_memory + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.web_terminal_id is not None: + result['WebTerminalId'] = self.web_terminal_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('CPU') is not None: - self.cpu = m.get('CPU') - if m.get('GPU') is not None: - self.gpu = m.get('GPU') - if m.get('GPUType') is not None: - self.gputype = m.get('GPUType') - if m.get('Memory') is not None: - self.memory = m.get('Memory') - if m.get('SharedMemory') is not None: - self.shared_memory = m.get('SharedMemory') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('WebTerminalId') is not None: + self.web_terminal_id = m.get('WebTerminalId') return self -class CreateTrainingJobRequestComputeResource(TeaModel): +class CreateInstanceWebTerminalResponse(TeaModel): def __init__( self, - ecs_count: int = None, - ecs_spec: str = None, - instance_count: int = None, - instance_spec: CreateTrainingJobRequestComputeResourceInstanceSpec = None, - resource_id: str = None, + headers: Dict[str, str] = None, + status_code: int = None, + body: CreateInstanceWebTerminalResponseBody = None, ): - self.ecs_count = ecs_count - self.ecs_spec = ecs_spec - self.instance_count = instance_count - self.instance_spec = instance_spec - self.resource_id = resource_id + self.headers = headers + self.status_code = status_code + self.body = body def validate(self): - if self.instance_spec: - self.instance_spec.validate() + if self.body: + self.body.validate() def to_map(self): _map = super().to_map() @@ -4146,41 +4899,33 @@ def to_map(self): return _map result = dict() - if self.ecs_count is not None: - result['EcsCount'] = self.ecs_count - if self.ecs_spec is not None: - result['EcsSpec'] = self.ecs_spec - if self.instance_count is not None: - result['InstanceCount'] = self.instance_count - if self.instance_spec is not None: - result['InstanceSpec'] = self.instance_spec.to_map() - if self.resource_id is not None: - result['ResourceId'] = self.resource_id + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() return result def from_map(self, m: dict = None): m = m or dict() - if m.get('EcsCount') is not None: - self.ecs_count = m.get('EcsCount') - if m.get('EcsSpec') is not None: - self.ecs_spec = m.get('EcsSpec') - if m.get('InstanceCount') is not None: - self.instance_count = m.get('InstanceCount') - if m.get('InstanceSpec') is not None: - temp_model = CreateTrainingJobRequestComputeResourceInstanceSpec() - self.instance_spec = temp_model.from_map(m['InstanceSpec']) - if m.get('ResourceId') is not None: - self.resource_id = m.get('ResourceId') + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = CreateInstanceWebTerminalResponseBody() + self.body = temp_model.from_map(m['body']) return self -class CreateTrainingJobRequestHyperParameters(TeaModel): +class CreateLLMProjectRequestLabels(TeaModel): def __init__( self, - name: str = None, + key: str = None, value: str = None, ): - self.name = name + self.key = key self.value = value def validate(self): @@ -4192,31 +4937,29 @@ def to_map(self): return _map result = dict() - if self.name is not None: - result['Name'] = self.name + if self.key is not None: + result['Key'] = self.key if self.value is not None: result['Value'] = self.value return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Name') is not None: - self.name = m.get('Name') + if m.get('Key') is not None: + self.key = m.get('Key') if m.get('Value') is not None: self.value = m.get('Value') return self -class CreateTrainingJobRequestInputChannels(TeaModel): +class CreateLLMProjectRequestRuntime(TeaModel): def __init__( self, - dataset_id: str = None, - input_uri: str = None, - name: str = None, + runtime_id: str = None, + runtime_type: str = None, ): - self.dataset_id = dataset_id - self.input_uri = input_uri - self.name = name + self.runtime_id = runtime_id + self.runtime_type = runtime_type def validate(self): pass @@ -4227,36 +4970,47 @@ def to_map(self): return _map result = dict() - if self.dataset_id is not None: - result['DatasetId'] = self.dataset_id - if self.input_uri is not None: - result['InputUri'] = self.input_uri - if self.name is not None: - result['Name'] = self.name + if self.runtime_id is not None: + result['RuntimeId'] = self.runtime_id + if self.runtime_type is not None: + result['RuntimeType'] = self.runtime_type return result def from_map(self, m: dict = None): m = m or dict() - if m.get('DatasetId') is not None: - self.dataset_id = m.get('DatasetId') - if m.get('InputUri') is not None: - self.input_uri = m.get('InputUri') - if m.get('Name') is not None: - self.name = m.get('Name') + if m.get('RuntimeId') is not None: + self.runtime_id = m.get('RuntimeId') + if m.get('RuntimeType') is not None: + self.runtime_type = m.get('RuntimeType') return self -class CreateTrainingJobRequestLabels(TeaModel): +class CreateLLMProjectRequest(TeaModel): def __init__( self, - key: str = None, - value: str = None, + labels: List[CreateLLMProjectRequestLabels] = None, + project_description: str = None, + project_name: str = None, + project_type: str = None, + root_path: str = None, + runtime: CreateLLMProjectRequestRuntime = None, + workspace_id: str = None, ): - self.key = key - self.value = value + self.labels = labels + self.project_description = project_description + self.project_name = project_name + self.project_type = project_type + self.root_path = root_path + self.runtime = runtime + self.workspace_id = workspace_id def validate(self): - pass + if self.labels: + for k in self.labels: + if k: + k.validate() + if self.runtime: + self.runtime.validate() def to_map(self): _map = super().to_map() @@ -4264,31 +5018,55 @@ def to_map(self): return _map result = dict() - if self.key is not None: - result['Key'] = self.key - if self.value is not None: - result['Value'] = self.value + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + if self.project_description is not None: + result['ProjectDescription'] = self.project_description + if self.project_name is not None: + result['ProjectName'] = self.project_name + if self.project_type is not None: + result['ProjectType'] = self.project_type + if self.root_path is not None: + result['RootPath'] = self.root_path + if self.runtime is not None: + result['Runtime'] = self.runtime.to_map() + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Key') is not None: - self.key = m.get('Key') - if m.get('Value') is not None: - self.value = m.get('Value') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = CreateLLMProjectRequestLabels() + self.labels.append(temp_model.from_map(k)) + if m.get('ProjectDescription') is not None: + self.project_description = m.get('ProjectDescription') + if m.get('ProjectName') is not None: + self.project_name = m.get('ProjectName') + if m.get('ProjectType') is not None: + self.project_type = m.get('ProjectType') + if m.get('RootPath') is not None: + self.root_path = m.get('RootPath') + if m.get('Runtime') is not None: + temp_model = CreateLLMProjectRequestRuntime() + self.runtime = temp_model.from_map(m['Runtime']) + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class CreateTrainingJobRequestOutputChannels(TeaModel): +class CreateLLMProjectResponseBody(TeaModel): def __init__( self, - dataset_id: str = None, - name: str = None, - output_uri: str = None, + project_id: str = None, + request_id: str = None, ): - self.dataset_id = dataset_id - self.name = name - self.output_uri = output_uri + self.project_id = project_id + self.request_id = request_id def validate(self): pass @@ -4299,34 +5077,35 @@ def to_map(self): return _map result = dict() - if self.dataset_id is not None: - result['DatasetId'] = self.dataset_id - if self.name is not None: - result['Name'] = self.name - if self.output_uri is not None: - result['OutputUri'] = self.output_uri + if self.project_id is not None: + result['ProjectId'] = self.project_id + if self.request_id is not None: + result['RequestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('DatasetId') is not None: - self.dataset_id = m.get('DatasetId') - if m.get('Name') is not None: - self.name = m.get('Name') - if m.get('OutputUri') is not None: - self.output_uri = m.get('OutputUri') + if m.get('ProjectId') is not None: + self.project_id = m.get('ProjectId') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') return self -class CreateTrainingJobRequestScheduler(TeaModel): +class CreateLLMProjectResponse(TeaModel): def __init__( self, - max_running_time_in_seconds: int = None, + headers: Dict[str, str] = None, + status_code: int = None, + body: CreateLLMProjectResponseBody = None, ): - self.max_running_time_in_seconds = max_running_time_in_seconds + self.headers = headers + self.status_code = status_code + self.body = body def validate(self): - pass + if self.body: + self.body.validate() def to_map(self): _map = super().to_map() @@ -4334,29 +5113,32 @@ def to_map(self): return _map result = dict() - if self.max_running_time_in_seconds is not None: - result['MaxRunningTimeInSeconds'] = self.max_running_time_in_seconds + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() return result def from_map(self, m: dict = None): m = m or dict() - if m.get('MaxRunningTimeInSeconds') is not None: - self.max_running_time_in_seconds = m.get('MaxRunningTimeInSeconds') + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = CreateLLMProjectResponseBody() + self.body = temp_model.from_map(m['body']) return self -class CreateTrainingJobRequestUserVpc(TeaModel): +class CreateLLMServiceIdentityRoleRequest(TeaModel): def __init__( self, - extended_cidrs: List[str] = None, - security_group_id: str = None, - switch_id: str = None, - vpc_id: str = None, + role_name: str = None, ): - self.extended_cidrs = extended_cidrs - self.security_group_id = security_group_id - self.switch_id = switch_id - self.vpc_id = vpc_id + self.role_name = role_name def validate(self): pass @@ -4367,204 +5149,25 @@ def to_map(self): return _map result = dict() - if self.extended_cidrs is not None: - result['ExtendedCIDRs'] = self.extended_cidrs - if self.security_group_id is not None: - result['SecurityGroupId'] = self.security_group_id - if self.switch_id is not None: - result['SwitchId'] = self.switch_id - if self.vpc_id is not None: - result['VpcId'] = self.vpc_id - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('ExtendedCIDRs') is not None: - self.extended_cidrs = m.get('ExtendedCIDRs') - if m.get('SecurityGroupId') is not None: - self.security_group_id = m.get('SecurityGroupId') - if m.get('SwitchId') is not None: - self.switch_id = m.get('SwitchId') - if m.get('VpcId') is not None: - self.vpc_id = m.get('VpcId') - return self - - -class CreateTrainingJobRequest(TeaModel): - def __init__( - self, - algorithm_name: str = None, - algorithm_provider: str = None, - algorithm_spec: AlgorithmSpec = None, - algorithm_version: str = None, - code_dir: Location = None, - compute_resource: CreateTrainingJobRequestComputeResource = None, - hyper_parameters: List[CreateTrainingJobRequestHyperParameters] = None, - input_channels: List[CreateTrainingJobRequestInputChannels] = None, - labels: List[CreateTrainingJobRequestLabels] = None, - output_channels: List[CreateTrainingJobRequestOutputChannels] = None, - role_arn: str = None, - scheduler: CreateTrainingJobRequestScheduler = None, - training_job_description: str = None, - training_job_name: str = None, - user_vpc: CreateTrainingJobRequestUserVpc = None, - workspace_id: str = None, - ): - self.algorithm_name = algorithm_name - self.algorithm_provider = algorithm_provider - self.algorithm_spec = algorithm_spec - self.algorithm_version = algorithm_version - self.code_dir = code_dir - self.compute_resource = compute_resource - self.hyper_parameters = hyper_parameters - self.input_channels = input_channels - self.labels = labels - self.output_channels = output_channels - self.role_arn = role_arn - self.scheduler = scheduler - self.training_job_description = training_job_description - self.training_job_name = training_job_name - self.user_vpc = user_vpc - self.workspace_id = workspace_id - - def validate(self): - if self.algorithm_spec: - self.algorithm_spec.validate() - if self.code_dir: - self.code_dir.validate() - if self.compute_resource: - self.compute_resource.validate() - if self.hyper_parameters: - for k in self.hyper_parameters: - if k: - k.validate() - if self.input_channels: - for k in self.input_channels: - if k: - k.validate() - if self.labels: - for k in self.labels: - if k: - k.validate() - if self.output_channels: - for k in self.output_channels: - if k: - k.validate() - if self.scheduler: - self.scheduler.validate() - if self.user_vpc: - self.user_vpc.validate() - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.algorithm_name is not None: - result['AlgorithmName'] = self.algorithm_name - if self.algorithm_provider is not None: - result['AlgorithmProvider'] = self.algorithm_provider - if self.algorithm_spec is not None: - result['AlgorithmSpec'] = self.algorithm_spec.to_map() - if self.algorithm_version is not None: - result['AlgorithmVersion'] = self.algorithm_version - if self.code_dir is not None: - result['CodeDir'] = self.code_dir.to_map() - if self.compute_resource is not None: - result['ComputeResource'] = self.compute_resource.to_map() - result['HyperParameters'] = [] - if self.hyper_parameters is not None: - for k in self.hyper_parameters: - result['HyperParameters'].append(k.to_map() if k else None) - result['InputChannels'] = [] - if self.input_channels is not None: - for k in self.input_channels: - result['InputChannels'].append(k.to_map() if k else None) - result['Labels'] = [] - if self.labels is not None: - for k in self.labels: - result['Labels'].append(k.to_map() if k else None) - result['OutputChannels'] = [] - if self.output_channels is not None: - for k in self.output_channels: - result['OutputChannels'].append(k.to_map() if k else None) - if self.role_arn is not None: - result['RoleArn'] = self.role_arn - if self.scheduler is not None: - result['Scheduler'] = self.scheduler.to_map() - if self.training_job_description is not None: - result['TrainingJobDescription'] = self.training_job_description - if self.training_job_name is not None: - result['TrainingJobName'] = self.training_job_name - if self.user_vpc is not None: - result['UserVpc'] = self.user_vpc.to_map() - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id + if self.role_name is not None: + result['RoleName'] = self.role_name return result def from_map(self, m: dict = None): m = m or dict() - if m.get('AlgorithmName') is not None: - self.algorithm_name = m.get('AlgorithmName') - if m.get('AlgorithmProvider') is not None: - self.algorithm_provider = m.get('AlgorithmProvider') - if m.get('AlgorithmSpec') is not None: - temp_model = AlgorithmSpec() - self.algorithm_spec = temp_model.from_map(m['AlgorithmSpec']) - if m.get('AlgorithmVersion') is not None: - self.algorithm_version = m.get('AlgorithmVersion') - if m.get('CodeDir') is not None: - temp_model = Location() - self.code_dir = temp_model.from_map(m['CodeDir']) - if m.get('ComputeResource') is not None: - temp_model = CreateTrainingJobRequestComputeResource() - self.compute_resource = temp_model.from_map(m['ComputeResource']) - self.hyper_parameters = [] - if m.get('HyperParameters') is not None: - for k in m.get('HyperParameters'): - temp_model = CreateTrainingJobRequestHyperParameters() - self.hyper_parameters.append(temp_model.from_map(k)) - self.input_channels = [] - if m.get('InputChannels') is not None: - for k in m.get('InputChannels'): - temp_model = CreateTrainingJobRequestInputChannels() - self.input_channels.append(temp_model.from_map(k)) - self.labels = [] - if m.get('Labels') is not None: - for k in m.get('Labels'): - temp_model = CreateTrainingJobRequestLabels() - self.labels.append(temp_model.from_map(k)) - self.output_channels = [] - if m.get('OutputChannels') is not None: - for k in m.get('OutputChannels'): - temp_model = CreateTrainingJobRequestOutputChannels() - self.output_channels.append(temp_model.from_map(k)) - if m.get('RoleArn') is not None: - self.role_arn = m.get('RoleArn') - if m.get('Scheduler') is not None: - temp_model = CreateTrainingJobRequestScheduler() - self.scheduler = temp_model.from_map(m['Scheduler']) - if m.get('TrainingJobDescription') is not None: - self.training_job_description = m.get('TrainingJobDescription') - if m.get('TrainingJobName') is not None: - self.training_job_name = m.get('TrainingJobName') - if m.get('UserVpc') is not None: - temp_model = CreateTrainingJobRequestUserVpc() - self.user_vpc = temp_model.from_map(m['UserVpc']) - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') + if m.get('RoleName') is not None: + self.role_name = m.get('RoleName') return self -class CreateTrainingJobResponseBody(TeaModel): +class CreateLLMServiceIdentityRoleResponseBody(TeaModel): def __init__( self, request_id: str = None, - training_job_id: str = None, + role_name: str = None, ): self.request_id = request_id - self.training_job_id = training_job_id + self.role_name = role_name def validate(self): pass @@ -4577,34 +5180,31 @@ def to_map(self): result = dict() if self.request_id is not None: result['RequestId'] = self.request_id - if self.training_job_id is not None: - result['TrainingJobId'] = self.training_job_id + if self.role_name is not None: + result['RoleName'] = self.role_name return result def from_map(self, m: dict = None): m = m or dict() if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('TrainingJobId') is not None: - self.training_job_id = m.get('TrainingJobId') + if m.get('RoleName') is not None: + self.role_name = m.get('RoleName') return self -class CreateTrainingJobResponse(TeaModel): +class CreateLLMServiceIdentityRoleResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: CreateTrainingJobResponseBody = None, + body: CreateLLMServiceIdentityRoleResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -4629,17 +5229,19 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = CreateTrainingJobResponseBody() + temp_model = CreateLLMServiceIdentityRoleResponseBody() self.body = temp_model.from_map(m['body']) return self -class DeleteAlgorithmResponseBody(TeaModel): +class CreateLLMSnapshotRequestStorage(TeaModel): def __init__( self, - request_id: str = None, + location: str = None, + type: str = None, ): - self.request_id = request_id + self.location = location + self.type = type def validate(self): pass @@ -4650,34 +5252,31 @@ def to_map(self): return _map result = dict() - if self.request_id is not None: - result['requestId'] = self.request_id + if self.location is not None: + result['Location'] = self.location + if self.type is not None: + result['Type'] = self.type return result def from_map(self, m: dict = None): m = m or dict() - if m.get('requestId') is not None: - self.request_id = m.get('requestId') + if m.get('Location') is not None: + self.location = m.get('Location') + if m.get('Type') is not None: + self.type = m.get('Type') return self -class DeleteAlgorithmResponse(TeaModel): +class CreateLLMSnapshotRequest(TeaModel): def __init__( self, - headers: Dict[str, str] = None, - status_code: int = None, - body: DeleteAlgorithmResponseBody = None, + storage: CreateLLMSnapshotRequestStorage = None, ): - self.headers = headers - self.status_code = status_code - self.body = body + self.storage = storage def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') - if self.body: - self.body.validate() + if self.storage: + self.storage.validate() def to_map(self): _map = super().to_map() @@ -4685,32 +5284,28 @@ def to_map(self): return _map result = dict() - if self.headers is not None: - result['headers'] = self.headers - if self.status_code is not None: - result['statusCode'] = self.status_code - if self.body is not None: - result['body'] = self.body.to_map() + if self.storage is not None: + result['Storage'] = self.storage.to_map() return result def from_map(self, m: dict = None): m = m or dict() - if m.get('headers') is not None: - self.headers = m.get('headers') - if m.get('statusCode') is not None: - self.status_code = m.get('statusCode') - if m.get('body') is not None: - temp_model = DeleteAlgorithmResponseBody() - self.body = temp_model.from_map(m['body']) + if m.get('Storage') is not None: + temp_model = CreateLLMSnapshotRequestStorage() + self.storage = temp_model.from_map(m['Storage']) return self -class DeleteAlgorithmVersionResponseBody(TeaModel): +class CreateLLMSnapshotResponseBody(TeaModel): def __init__( self, + pipeline_run_id: str = None, request_id: str = None, + snapshot_id: str = None, ): + self.pipeline_run_id = pipeline_run_id self.request_id = request_id + self.snapshot_id = snapshot_id def validate(self): pass @@ -4721,32 +5316,37 @@ def to_map(self): return _map result = dict() + if self.pipeline_run_id is not None: + result['PipelineRunId'] = self.pipeline_run_id if self.request_id is not None: result['RequestId'] = self.request_id + if self.snapshot_id is not None: + result['SnapshotId'] = self.snapshot_id return result def from_map(self, m: dict = None): m = m or dict() + if m.get('PipelineRunId') is not None: + self.pipeline_run_id = m.get('PipelineRunId') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') + if m.get('SnapshotId') is not None: + self.snapshot_id = m.get('SnapshotId') return self -class DeleteAlgorithmVersionResponse(TeaModel): +class CreateLLMSnapshotResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: DeleteAlgorithmVersionResponseBody = None, + body: CreateLLMSnapshotResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -4771,16 +5371,114 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = DeleteAlgorithmVersionResponseBody() + temp_model = CreateLLMSnapshotResponseBody() self.body = temp_model.from_map(m['body']) return self -class DeleteComponentResponseBody(TeaModel): +class CreateQuotaRequest(TeaModel): + def __init__( + self, + allocate_strategy: str = None, + description: str = None, + labels: List[Label] = None, + min: ResourceSpec = None, + parent_quota_id: str = None, + queue_strategy: str = None, + quota_config: QuotaConfig = None, + quota_name: str = None, + resource_group_ids: List[str] = None, + resource_type: str = None, + ): + self.allocate_strategy = allocate_strategy + self.description = description + self.labels = labels + self.min = min + self.parent_quota_id = parent_quota_id + self.queue_strategy = queue_strategy + self.quota_config = quota_config + self.quota_name = quota_name + self.resource_group_ids = resource_group_ids + self.resource_type = resource_type + + def validate(self): + if self.labels: + for k in self.labels: + if k: + k.validate() + if self.min: + self.min.validate() + if self.quota_config: + self.quota_config.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.allocate_strategy is not None: + result['AllocateStrategy'] = self.allocate_strategy + if self.description is not None: + result['Description'] = self.description + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + if self.min is not None: + result['Min'] = self.min.to_map() + if self.parent_quota_id is not None: + result['ParentQuotaId'] = self.parent_quota_id + if self.queue_strategy is not None: + result['QueueStrategy'] = self.queue_strategy + if self.quota_config is not None: + result['QuotaConfig'] = self.quota_config.to_map() + if self.quota_name is not None: + result['QuotaName'] = self.quota_name + if self.resource_group_ids is not None: + result['ResourceGroupIds'] = self.resource_group_ids + if self.resource_type is not None: + result['ResourceType'] = self.resource_type + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('AllocateStrategy') is not None: + self.allocate_strategy = m.get('AllocateStrategy') + if m.get('Description') is not None: + self.description = m.get('Description') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = Label() + self.labels.append(temp_model.from_map(k)) + if m.get('Min') is not None: + temp_model = ResourceSpec() + self.min = temp_model.from_map(m['Min']) + if m.get('ParentQuotaId') is not None: + self.parent_quota_id = m.get('ParentQuotaId') + if m.get('QueueStrategy') is not None: + self.queue_strategy = m.get('QueueStrategy') + if m.get('QuotaConfig') is not None: + temp_model = QuotaConfig() + self.quota_config = temp_model.from_map(m['QuotaConfig']) + if m.get('QuotaName') is not None: + self.quota_name = m.get('QuotaName') + if m.get('ResourceGroupIds') is not None: + self.resource_group_ids = m.get('ResourceGroupIds') + if m.get('ResourceType') is not None: + self.resource_type = m.get('ResourceType') + return self + + +class CreateQuotaResponseBody(TeaModel): def __init__( self, + quota_id: str = None, request_id: str = None, ): + # Quota Id + self.quota_id = quota_id self.request_id = request_id def validate(self): @@ -4792,32 +5490,33 @@ def to_map(self): return _map result = dict() + if self.quota_id is not None: + result['QuotaId'] = self.quota_id if self.request_id is not None: result['RequestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() + if m.get('QuotaId') is not None: + self.quota_id = m.get('QuotaId') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') return self -class DeleteComponentResponse(TeaModel): +class CreateQuotaResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: DeleteComponentResponseBody = None, + body: CreateQuotaResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -4842,17 +5541,19 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = DeleteComponentResponseBody() + temp_model = CreateQuotaResponseBody() self.body = temp_model.from_map(m['body']) return self -class DeleteComponentVersionResponseBody(TeaModel): +class CreateResourceGroupRequestTag(TeaModel): def __init__( self, - request_id: str = None, + key: str = None, + value: str = None, ): - self.request_id = request_id + self.key = key + self.value = value def validate(self): pass @@ -4863,34 +5564,45 @@ def to_map(self): return _map result = dict() - if self.request_id is not None: - result['RequestId'] = self.request_id + if self.key is not None: + result['Key'] = self.key + if self.value is not None: + result['Value'] = self.value return result def from_map(self, m: dict = None): m = m or dict() - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') + if m.get('Key') is not None: + self.key = m.get('Key') + if m.get('Value') is not None: + self.value = m.get('Value') return self -class DeleteComponentVersionResponse(TeaModel): +class CreateResourceGroupRequest(TeaModel): def __init__( self, - headers: Dict[str, str] = None, - status_code: int = None, - body: DeleteComponentVersionResponseBody = None, + computing_resource_provider: str = None, + description: str = None, + name: str = None, + resource_type: str = None, + tag: List[CreateResourceGroupRequestTag] = None, + user_vpc: UserVpc = None, ): - self.headers = headers - self.status_code = status_code - self.body = body + self.computing_resource_provider = computing_resource_provider + self.description = description + self.name = name + self.resource_type = resource_type + self.tag = tag + self.user_vpc = user_vpc def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') - if self.body: - self.body.validate() + if self.tag: + for k in self.tag: + if k: + k.validate() + if self.user_vpc: + self.user_vpc.validate() def to_map(self): _map = super().to_map() @@ -4898,32 +5610,51 @@ def to_map(self): return _map result = dict() - if self.headers is not None: - result['headers'] = self.headers - if self.status_code is not None: - result['statusCode'] = self.status_code - if self.body is not None: - result['body'] = self.body.to_map() + if self.computing_resource_provider is not None: + result['ComputingResourceProvider'] = self.computing_resource_provider + if self.description is not None: + result['Description'] = self.description + if self.name is not None: + result['Name'] = self.name + if self.resource_type is not None: + result['ResourceType'] = self.resource_type + result['Tag'] = [] + if self.tag is not None: + for k in self.tag: + result['Tag'].append(k.to_map() if k else None) + if self.user_vpc is not None: + result['UserVpc'] = self.user_vpc.to_map() return result def from_map(self, m: dict = None): m = m or dict() - if m.get('headers') is not None: - self.headers = m.get('headers') - if m.get('statusCode') is not None: - self.status_code = m.get('statusCode') - if m.get('body') is not None: - temp_model = DeleteComponentVersionResponseBody() - self.body = temp_model.from_map(m['body']) + if m.get('ComputingResourceProvider') is not None: + self.computing_resource_provider = m.get('ComputingResourceProvider') + if m.get('Description') is not None: + self.description = m.get('Description') + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('ResourceType') is not None: + self.resource_type = m.get('ResourceType') + self.tag = [] + if m.get('Tag') is not None: + for k in m.get('Tag'): + temp_model = CreateResourceGroupRequestTag() + self.tag.append(temp_model.from_map(k)) + if m.get('UserVpc') is not None: + temp_model = UserVpc() + self.user_vpc = temp_model.from_map(m['UserVpc']) return self -class DeleteComponentVersionSnapshotResponseBody(TeaModel): +class CreateResourceGroupResponseBody(TeaModel): def __init__( self, request_id: str = None, + resource_group_id: str = None, ): self.request_id = request_id + self.resource_group_id = resource_group_id def validate(self): pass @@ -4936,30 +5667,31 @@ def to_map(self): result = dict() if self.request_id is not None: result['RequestId'] = self.request_id + if self.resource_group_id is not None: + result['ResourceGroupID'] = self.resource_group_id return result def from_map(self, m: dict = None): m = m or dict() if m.get('RequestId') is not None: self.request_id = m.get('RequestId') + if m.get('ResourceGroupID') is not None: + self.resource_group_id = m.get('ResourceGroupID') return self -class DeleteComponentVersionSnapshotResponse(TeaModel): +class CreateResourceGroupResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: DeleteComponentVersionSnapshotResponseBody = None, + body: CreateResourceGroupResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -4984,19 +5716,19 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = DeleteComponentVersionSnapshotResponseBody() + temp_model = CreateResourceGroupResponseBody() self.body = temp_model.from_map(m['body']) return self -class DeleteMachineGroupResponseBody(TeaModel): +class CreateResourceGroupMachineGroupRequestTag(TeaModel): def __init__( self, - machine_group_id: str = None, - request_id: str = None, + key: str = None, + value: str = None, ): - self.machine_group_id = machine_group_id - self.request_id = request_id + self.key = key + self.value = value def validate(self): pass @@ -5007,38 +5739,45 @@ def to_map(self): return _map result = dict() - if self.machine_group_id is not None: - result['MachineGroupID'] = self.machine_group_id - if self.request_id is not None: - result['RequestId'] = self.request_id + if self.key is not None: + result['Key'] = self.key + if self.value is not None: + result['Value'] = self.value return result def from_map(self, m: dict = None): m = m or dict() - if m.get('MachineGroupID') is not None: - self.machine_group_id = m.get('MachineGroupID') - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') + if m.get('Key') is not None: + self.key = m.get('Key') + if m.get('Value') is not None: + self.value = m.get('Value') return self -class DeleteMachineGroupResponse(TeaModel): +class CreateResourceGroupMachineGroupRequest(TeaModel): def __init__( self, - headers: Dict[str, str] = None, - status_code: int = None, - body: DeleteMachineGroupResponseBody = None, + ecs_count: int = None, + ecs_spec: str = None, + name: str = None, + payment_duration: str = None, + payment_duration_unit: str = None, + payment_type: str = None, + tag: List[CreateResourceGroupMachineGroupRequestTag] = None, ): - self.headers = headers - self.status_code = status_code - self.body = body - + self.ecs_count = ecs_count + self.ecs_spec = ecs_spec + self.name = name + self.payment_duration = payment_duration + self.payment_duration_unit = payment_duration_unit + self.payment_type = payment_type + self.tag = tag + def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') - if self.body: - self.body.validate() + if self.tag: + for k in self.tag: + if k: + k.validate() def to_map(self): _map = super().to_map() @@ -5046,33 +5785,53 @@ def to_map(self): return _map result = dict() - if self.headers is not None: - result['headers'] = self.headers - if self.status_code is not None: - result['statusCode'] = self.status_code - if self.body is not None: - result['body'] = self.body.to_map() + if self.ecs_count is not None: + result['EcsCount'] = self.ecs_count + if self.ecs_spec is not None: + result['EcsSpec'] = self.ecs_spec + if self.name is not None: + result['Name'] = self.name + if self.payment_duration is not None: + result['PaymentDuration'] = self.payment_duration + if self.payment_duration_unit is not None: + result['PaymentDurationUnit'] = self.payment_duration_unit + if self.payment_type is not None: + result['PaymentType'] = self.payment_type + result['Tag'] = [] + if self.tag is not None: + for k in self.tag: + result['Tag'].append(k.to_map() if k else None) return result def from_map(self, m: dict = None): m = m or dict() - if m.get('headers') is not None: - self.headers = m.get('headers') - if m.get('statusCode') is not None: - self.status_code = m.get('statusCode') - if m.get('body') is not None: - temp_model = DeleteMachineGroupResponseBody() - self.body = temp_model.from_map(m['body']) + if m.get('EcsCount') is not None: + self.ecs_count = m.get('EcsCount') + if m.get('EcsSpec') is not None: + self.ecs_spec = m.get('EcsSpec') + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('PaymentDuration') is not None: + self.payment_duration = m.get('PaymentDuration') + if m.get('PaymentDurationUnit') is not None: + self.payment_duration_unit = m.get('PaymentDurationUnit') + if m.get('PaymentType') is not None: + self.payment_type = m.get('PaymentType') + self.tag = [] + if m.get('Tag') is not None: + for k in m.get('Tag'): + temp_model = CreateResourceGroupMachineGroupRequestTag() + self.tag.append(temp_model.from_map(k)) return self -class DeleteQuotaResponseBody(TeaModel): +class CreateResourceGroupMachineGroupResponseBody(TeaModel): def __init__( self, - quota_id: str = None, + machine_group_id: str = None, request_id: str = None, ): - self.quota_id = quota_id + self.machine_group_id = machine_group_id self.request_id = request_id def validate(self): @@ -5084,36 +5843,33 @@ def to_map(self): return _map result = dict() - if self.quota_id is not None: - result['QuotaId'] = self.quota_id + if self.machine_group_id is not None: + result['MachineGroupID'] = self.machine_group_id if self.request_id is not None: result['RequestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('QuotaId') is not None: - self.quota_id = m.get('QuotaId') + if m.get('MachineGroupID') is not None: + self.machine_group_id = m.get('MachineGroupID') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') return self -class DeleteQuotaResponse(TeaModel): +class CreateResourceGroupMachineGroupResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: DeleteQuotaResponseBody = None, + body: CreateResourceGroupMachineGroupResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -5138,17 +5894,17 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = DeleteQuotaResponseBody() + temp_model = CreateResourceGroupMachineGroupResponseBody() self.body = temp_model.from_map(m['body']) return self -class DeleteQuotaLabelsRequest(TeaModel): +class CreateServiceIdentityRoleRequest(TeaModel): def __init__( self, - keys: str = None, + role_name: str = None, ): - self.keys = keys + self.role_name = role_name def validate(self): pass @@ -5159,25 +5915,25 @@ def to_map(self): return _map result = dict() - if self.keys is not None: - result['Keys'] = self.keys + if self.role_name is not None: + result['RoleName'] = self.role_name return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Keys') is not None: - self.keys = m.get('Keys') + if m.get('RoleName') is not None: + self.role_name = m.get('RoleName') return self -class DeleteQuotaLabelsResponseBody(TeaModel): +class CreateServiceIdentityRoleResponseBody(TeaModel): def __init__( self, - quota_id: str = None, request_id: str = None, + role_name: str = None, ): - self.quota_id = quota_id self.request_id = request_id + self.role_name = role_name def validate(self): pass @@ -5188,36 +5944,33 @@ def to_map(self): return _map result = dict() - if self.quota_id is not None: - result['QuotaId'] = self.quota_id if self.request_id is not None: result['RequestId'] = self.request_id + if self.role_name is not None: + result['RoleName'] = self.role_name return result def from_map(self, m: dict = None): m = m or dict() - if m.get('QuotaId') is not None: - self.quota_id = m.get('QuotaId') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') + if m.get('RoleName') is not None: + self.role_name = m.get('RoleName') return self -class DeleteQuotaLabelsResponse(TeaModel): +class CreateServiceIdentityRoleResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: DeleteQuotaLabelsResponseBody = None, + body: CreateServiceIdentityRoleResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -5242,19 +5995,25 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = DeleteQuotaLabelsResponseBody() + temp_model = CreateServiceIdentityRoleResponseBody() self.body = temp_model.from_map(m['body']) return self -class DeleteResourceGroupResponseBody(TeaModel): +class CreateTrainingJobRequestComputeResourceInstanceSpec(TeaModel): def __init__( self, - request_id: str = None, - resource_group_id: str = None, + cpu: str = None, + gpu: str = None, + gputype: str = None, + memory: str = None, + shared_memory: str = None, ): - self.request_id = request_id - self.resource_group_id = resource_group_id + self.cpu = cpu + self.gpu = gpu + self.gputype = gputype + self.memory = memory + self.shared_memory = shared_memory def validate(self): pass @@ -5265,38 +6024,51 @@ def to_map(self): return _map result = dict() - if self.request_id is not None: - result['RequestId'] = self.request_id - if self.resource_group_id is not None: - result['ResourceGroupID'] = self.resource_group_id + if self.cpu is not None: + result['CPU'] = self.cpu + if self.gpu is not None: + result['GPU'] = self.gpu + if self.gputype is not None: + result['GPUType'] = self.gputype + if self.memory is not None: + result['Memory'] = self.memory + if self.shared_memory is not None: + result['SharedMemory'] = self.shared_memory return result def from_map(self, m: dict = None): m = m or dict() - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - if m.get('ResourceGroupID') is not None: - self.resource_group_id = m.get('ResourceGroupID') + if m.get('CPU') is not None: + self.cpu = m.get('CPU') + if m.get('GPU') is not None: + self.gpu = m.get('GPU') + if m.get('GPUType') is not None: + self.gputype = m.get('GPUType') + if m.get('Memory') is not None: + self.memory = m.get('Memory') + if m.get('SharedMemory') is not None: + self.shared_memory = m.get('SharedMemory') return self -class DeleteResourceGroupResponse(TeaModel): +class CreateTrainingJobRequestComputeResource(TeaModel): def __init__( self, - headers: Dict[str, str] = None, - status_code: int = None, - body: DeleteResourceGroupResponseBody = None, + ecs_count: int = None, + ecs_spec: str = None, + instance_count: int = None, + instance_spec: CreateTrainingJobRequestComputeResourceInstanceSpec = None, + resource_id: str = None, ): - self.headers = headers - self.status_code = status_code - self.body = body + self.ecs_count = ecs_count + self.ecs_spec = ecs_spec + self.instance_count = instance_count + self.instance_spec = instance_spec + self.resource_id = resource_id def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') - if self.body: - self.body.validate() + if self.instance_spec: + self.instance_spec.validate() def to_map(self): _map = super().to_map() @@ -5304,34 +6076,40 @@ def to_map(self): return _map result = dict() - if self.headers is not None: - result['headers'] = self.headers - if self.status_code is not None: - result['statusCode'] = self.status_code - if self.body is not None: - result['body'] = self.body.to_map() + if self.ecs_count is not None: + result['EcsCount'] = self.ecs_count + if self.ecs_spec is not None: + result['EcsSpec'] = self.ecs_spec + if self.instance_count is not None: + result['InstanceCount'] = self.instance_count + if self.instance_spec is not None: + result['InstanceSpec'] = self.instance_spec.to_map() + if self.resource_id is not None: + result['ResourceId'] = self.resource_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('headers') is not None: - self.headers = m.get('headers') - if m.get('statusCode') is not None: - self.status_code = m.get('statusCode') - if m.get('body') is not None: - temp_model = DeleteResourceGroupResponseBody() - self.body = temp_model.from_map(m['body']) + if m.get('EcsCount') is not None: + self.ecs_count = m.get('EcsCount') + if m.get('EcsSpec') is not None: + self.ecs_spec = m.get('EcsSpec') + if m.get('InstanceCount') is not None: + self.instance_count = m.get('InstanceCount') + if m.get('InstanceSpec') is not None: + temp_model = CreateTrainingJobRequestComputeResourceInstanceSpec() + self.instance_spec = temp_model.from_map(m['InstanceSpec']) + if m.get('ResourceId') is not None: + self.resource_id = m.get('ResourceId') return self -class DeleteResourceGroupMachineGroupResponseBody(TeaModel): +class CreateTrainingJobRequestExperimentConfig(TeaModel): def __init__( self, - machine_group_id: str = None, - request_id: str = None, + experiment_id: str = None, ): - self.machine_group_id = machine_group_id - self.request_id = request_id + self.experiment_id = experiment_id def validate(self): pass @@ -5342,38 +6120,28 @@ def to_map(self): return _map result = dict() - if self.machine_group_id is not None: - result['MachineGroupID'] = self.machine_group_id - if self.request_id is not None: - result['RequestId'] = self.request_id + if self.experiment_id is not None: + result['ExperimentId'] = self.experiment_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('MachineGroupID') is not None: - self.machine_group_id = m.get('MachineGroupID') - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') + if m.get('ExperimentId') is not None: + self.experiment_id = m.get('ExperimentId') return self -class DeleteResourceGroupMachineGroupResponse(TeaModel): +class CreateTrainingJobRequestHyperParameters(TeaModel): def __init__( self, - headers: Dict[str, str] = None, - status_code: int = None, - body: DeleteResourceGroupMachineGroupResponseBody = None, + name: str = None, + value: str = None, ): - self.headers = headers - self.status_code = status_code - self.body = body + self.name = name + self.value = value def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') - if self.body: - self.body.validate() + pass def to_map(self): _map = super().to_map() @@ -5381,34 +6149,33 @@ def to_map(self): return _map result = dict() - if self.headers is not None: - result['headers'] = self.headers - if self.status_code is not None: - result['statusCode'] = self.status_code - if self.body is not None: - result['body'] = self.body.to_map() + if self.name is not None: + result['Name'] = self.name + if self.value is not None: + result['Value'] = self.value return result def from_map(self, m: dict = None): m = m or dict() - if m.get('headers') is not None: - self.headers = m.get('headers') - if m.get('statusCode') is not None: - self.status_code = m.get('statusCode') - if m.get('body') is not None: - temp_model = DeleteResourceGroupMachineGroupResponseBody() - self.body = temp_model.from_map(m['body']) + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('Value') is not None: + self.value = m.get('Value') return self -class DeleteTrainingJobResponseBody(TeaModel): +class CreateTrainingJobRequestInputChannels(TeaModel): def __init__( self, - request_id: str = None, + dataset_id: str = None, + input_uri: str = None, + name: str = None, ): - self.request_id = request_id - - def validate(self): + self.dataset_id = dataset_id + self.input_uri = input_uri + self.name = name + + def validate(self): pass def to_map(self): @@ -5417,34 +6184,36 @@ def to_map(self): return _map result = dict() - if self.request_id is not None: - result['RequestId'] = self.request_id + if self.dataset_id is not None: + result['DatasetId'] = self.dataset_id + if self.input_uri is not None: + result['InputUri'] = self.input_uri + if self.name is not None: + result['Name'] = self.name return result def from_map(self, m: dict = None): m = m or dict() - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') + if m.get('DatasetId') is not None: + self.dataset_id = m.get('DatasetId') + if m.get('InputUri') is not None: + self.input_uri = m.get('InputUri') + if m.get('Name') is not None: + self.name = m.get('Name') return self -class DeleteTrainingJobResponse(TeaModel): +class CreateTrainingJobRequestLabels(TeaModel): def __init__( self, - headers: Dict[str, str] = None, - status_code: int = None, - body: DeleteTrainingJobResponseBody = None, + key: str = None, + value: str = None, ): - self.headers = headers - self.status_code = status_code - self.body = body + self.key = key + self.value = value def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') - if self.body: - self.body.validate() + pass def to_map(self): _map = super().to_map() @@ -5452,32 +6221,31 @@ def to_map(self): return _map result = dict() - if self.headers is not None: - result['headers'] = self.headers - if self.status_code is not None: - result['statusCode'] = self.status_code - if self.body is not None: - result['body'] = self.body.to_map() + if self.key is not None: + result['Key'] = self.key + if self.value is not None: + result['Value'] = self.value return result def from_map(self, m: dict = None): m = m or dict() - if m.get('headers') is not None: - self.headers = m.get('headers') - if m.get('statusCode') is not None: - self.status_code = m.get('statusCode') - if m.get('body') is not None: - temp_model = DeleteTrainingJobResponseBody() - self.body = temp_model.from_map(m['body']) + if m.get('Key') is not None: + self.key = m.get('Key') + if m.get('Value') is not None: + self.value = m.get('Value') return self -class DeleteTrainingJobLabelsRequest(TeaModel): +class CreateTrainingJobRequestOutputChannels(TeaModel): def __init__( self, - keys: str = None, + dataset_id: str = None, + name: str = None, + output_uri: str = None, ): - self.keys = keys + self.dataset_id = dataset_id + self.name = name + self.output_uri = output_uri def validate(self): pass @@ -5488,23 +6256,31 @@ def to_map(self): return _map result = dict() - if self.keys is not None: - result['Keys'] = self.keys + if self.dataset_id is not None: + result['DatasetId'] = self.dataset_id + if self.name is not None: + result['Name'] = self.name + if self.output_uri is not None: + result['OutputUri'] = self.output_uri return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Keys') is not None: - self.keys = m.get('Keys') + if m.get('DatasetId') is not None: + self.dataset_id = m.get('DatasetId') + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('OutputUri') is not None: + self.output_uri = m.get('OutputUri') return self -class DeleteTrainingJobLabelsResponseBody(TeaModel): +class CreateTrainingJobRequestScheduler(TeaModel): def __init__( self, - request_id: str = None, + max_running_time_in_seconds: int = None, ): - self.request_id = request_id + self.max_running_time_in_seconds = max_running_time_in_seconds def validate(self): pass @@ -5515,34 +6291,32 @@ def to_map(self): return _map result = dict() - if self.request_id is not None: - result['RequestId'] = self.request_id + if self.max_running_time_in_seconds is not None: + result['MaxRunningTimeInSeconds'] = self.max_running_time_in_seconds return result def from_map(self, m: dict = None): m = m or dict() - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') + if m.get('MaxRunningTimeInSeconds') is not None: + self.max_running_time_in_seconds = m.get('MaxRunningTimeInSeconds') return self -class DeleteTrainingJobLabelsResponse(TeaModel): +class CreateTrainingJobRequestSettings(TeaModel): def __init__( self, - headers: Dict[str, str] = None, - status_code: int = None, - body: DeleteTrainingJobLabelsResponseBody = None, + aimaster_type: str = None, + enable_error_monitoring_in_aimaster: bool = None, + error_monitoring_args: str = None, + priority: int = None, ): - self.headers = headers - self.status_code = status_code - self.body = body + self.aimaster_type = aimaster_type + self.enable_error_monitoring_in_aimaster = enable_error_monitoring_in_aimaster + self.error_monitoring_args = error_monitoring_args + self.priority = priority def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') - if self.body: - self.body.validate() + pass def to_map(self): _map = super().to_map() @@ -5550,38 +6324,43 @@ def to_map(self): return _map result = dict() - if self.headers is not None: - result['headers'] = self.headers - if self.status_code is not None: - result['statusCode'] = self.status_code - if self.body is not None: - result['body'] = self.body.to_map() + if self.aimaster_type is not None: + result['AIMasterType'] = self.aimaster_type + if self.enable_error_monitoring_in_aimaster is not None: + result['EnableErrorMonitoringInAIMaster'] = self.enable_error_monitoring_in_aimaster + if self.error_monitoring_args is not None: + result['ErrorMonitoringArgs'] = self.error_monitoring_args + if self.priority is not None: + result['Priority'] = self.priority return result def from_map(self, m: dict = None): m = m or dict() - if m.get('headers') is not None: - self.headers = m.get('headers') - if m.get('statusCode') is not None: - self.status_code = m.get('statusCode') - if m.get('body') is not None: - temp_model = DeleteTrainingJobLabelsResponseBody() - self.body = temp_model.from_map(m['body']) + if m.get('AIMasterType') is not None: + self.aimaster_type = m.get('AIMasterType') + if m.get('EnableErrorMonitoringInAIMaster') is not None: + self.enable_error_monitoring_in_aimaster = m.get('EnableErrorMonitoringInAIMaster') + if m.get('ErrorMonitoringArgs') is not None: + self.error_monitoring_args = m.get('ErrorMonitoringArgs') + if m.get('Priority') is not None: + self.priority = m.get('Priority') return self -class GetAI4DDefaultBucketResponseBody(TeaModel): +class CreateTrainingJobRequestUserVpc(TeaModel): def __init__( self, - extranet_endpoint: str = None, - intranet_endpoint: str = None, - name: str = None, - request_id: str = None, + default_route: str = None, + extended_cidrs: List[str] = None, + security_group_id: str = None, + switch_id: str = None, + vpc_id: str = None, ): - self.extranet_endpoint = extranet_endpoint - self.intranet_endpoint = intranet_endpoint - self.name = name - self.request_id = request_id + self.default_route = default_route + self.extended_cidrs = extended_cidrs + self.security_group_id = security_group_id + self.switch_id = switch_id + self.vpc_id = vpc_id def validate(self): pass @@ -5592,46 +6371,109 @@ def to_map(self): return _map result = dict() - if self.extranet_endpoint is not None: - result['ExtranetEndpoint'] = self.extranet_endpoint - if self.intranet_endpoint is not None: - result['IntranetEndpoint'] = self.intranet_endpoint - if self.name is not None: - result['Name'] = self.name - if self.request_id is not None: - result['RequestId'] = self.request_id + if self.default_route is not None: + result['DefaultRoute'] = self.default_route + if self.extended_cidrs is not None: + result['ExtendedCIDRs'] = self.extended_cidrs + if self.security_group_id is not None: + result['SecurityGroupId'] = self.security_group_id + if self.switch_id is not None: + result['SwitchId'] = self.switch_id + if self.vpc_id is not None: + result['VpcId'] = self.vpc_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ExtranetEndpoint') is not None: - self.extranet_endpoint = m.get('ExtranetEndpoint') - if m.get('IntranetEndpoint') is not None: - self.intranet_endpoint = m.get('IntranetEndpoint') - if m.get('Name') is not None: - self.name = m.get('Name') - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') + if m.get('DefaultRoute') is not None: + self.default_route = m.get('DefaultRoute') + if m.get('ExtendedCIDRs') is not None: + self.extended_cidrs = m.get('ExtendedCIDRs') + if m.get('SecurityGroupId') is not None: + self.security_group_id = m.get('SecurityGroupId') + if m.get('SwitchId') is not None: + self.switch_id = m.get('SwitchId') + if m.get('VpcId') is not None: + self.vpc_id = m.get('VpcId') return self -class GetAI4DDefaultBucketResponse(TeaModel): +class CreateTrainingJobRequest(TeaModel): def __init__( self, - headers: Dict[str, str] = None, - status_code: int = None, - body: GetAI4DDefaultBucketResponseBody = None, + algorithm_name: str = None, + algorithm_provider: str = None, + algorithm_spec: AlgorithmSpec = None, + algorithm_version: str = None, + code_dir: Location = None, + compute_resource: CreateTrainingJobRequestComputeResource = None, + environments: Dict[str, str] = None, + experiment_config: CreateTrainingJobRequestExperimentConfig = None, + hyper_parameters: List[CreateTrainingJobRequestHyperParameters] = None, + input_channels: List[CreateTrainingJobRequestInputChannels] = None, + labels: List[CreateTrainingJobRequestLabels] = None, + output_channels: List[CreateTrainingJobRequestOutputChannels] = None, + python_requirements: List[str] = None, + role_arn: str = None, + scheduler: CreateTrainingJobRequestScheduler = None, + settings: CreateTrainingJobRequestSettings = None, + training_job_description: str = None, + training_job_name: str = None, + user_vpc: CreateTrainingJobRequestUserVpc = None, + workspace_id: str = None, ): - self.headers = headers - self.status_code = status_code - self.body = body + self.algorithm_name = algorithm_name + self.algorithm_provider = algorithm_provider + self.algorithm_spec = algorithm_spec + self.algorithm_version = algorithm_version + self.code_dir = code_dir + self.compute_resource = compute_resource + self.environments = environments + self.experiment_config = experiment_config + self.hyper_parameters = hyper_parameters + self.input_channels = input_channels + self.labels = labels + self.output_channels = output_channels + self.python_requirements = python_requirements + self.role_arn = role_arn + self.scheduler = scheduler + self.settings = settings + self.training_job_description = training_job_description + self.training_job_name = training_job_name + self.user_vpc = user_vpc + self.workspace_id = workspace_id def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') - if self.body: - self.body.validate() + if self.algorithm_spec: + self.algorithm_spec.validate() + if self.code_dir: + self.code_dir.validate() + if self.compute_resource: + self.compute_resource.validate() + if self.experiment_config: + self.experiment_config.validate() + if self.hyper_parameters: + for k in self.hyper_parameters: + if k: + k.validate() + if self.input_channels: + for k in self.input_channels: + if k: + k.validate() + if self.labels: + for k in self.labels: + if k: + k.validate() + if self.output_channels: + for k in self.output_channels: + if k: + k.validate() + if self.scheduler: + self.scheduler.validate() + if self.settings: + self.settings.validate() + if self.user_vpc: + self.user_vpc.validate() def to_map(self): _map = super().to_map() @@ -5639,52 +6481,128 @@ def to_map(self): return _map result = dict() - if self.headers is not None: - result['headers'] = self.headers - if self.status_code is not None: - result['statusCode'] = self.status_code - if self.body is not None: - result['body'] = self.body.to_map() - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('headers') is not None: - self.headers = m.get('headers') - if m.get('statusCode') is not None: - self.status_code = m.get('statusCode') - if m.get('body') is not None: - temp_model = GetAI4DDefaultBucketResponseBody() - self.body = temp_model.from_map(m['body']) + if self.algorithm_name is not None: + result['AlgorithmName'] = self.algorithm_name + if self.algorithm_provider is not None: + result['AlgorithmProvider'] = self.algorithm_provider + if self.algorithm_spec is not None: + result['AlgorithmSpec'] = self.algorithm_spec.to_map() + if self.algorithm_version is not None: + result['AlgorithmVersion'] = self.algorithm_version + if self.code_dir is not None: + result['CodeDir'] = self.code_dir.to_map() + if self.compute_resource is not None: + result['ComputeResource'] = self.compute_resource.to_map() + if self.environments is not None: + result['Environments'] = self.environments + if self.experiment_config is not None: + result['ExperimentConfig'] = self.experiment_config.to_map() + result['HyperParameters'] = [] + if self.hyper_parameters is not None: + for k in self.hyper_parameters: + result['HyperParameters'].append(k.to_map() if k else None) + result['InputChannels'] = [] + if self.input_channels is not None: + for k in self.input_channels: + result['InputChannels'].append(k.to_map() if k else None) + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + result['OutputChannels'] = [] + if self.output_channels is not None: + for k in self.output_channels: + result['OutputChannels'].append(k.to_map() if k else None) + if self.python_requirements is not None: + result['PythonRequirements'] = self.python_requirements + if self.role_arn is not None: + result['RoleArn'] = self.role_arn + if self.scheduler is not None: + result['Scheduler'] = self.scheduler.to_map() + if self.settings is not None: + result['Settings'] = self.settings.to_map() + if self.training_job_description is not None: + result['TrainingJobDescription'] = self.training_job_description + if self.training_job_name is not None: + result['TrainingJobName'] = self.training_job_name + if self.user_vpc is not None: + result['UserVpc'] = self.user_vpc.to_map() + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('AlgorithmName') is not None: + self.algorithm_name = m.get('AlgorithmName') + if m.get('AlgorithmProvider') is not None: + self.algorithm_provider = m.get('AlgorithmProvider') + if m.get('AlgorithmSpec') is not None: + temp_model = AlgorithmSpec() + self.algorithm_spec = temp_model.from_map(m['AlgorithmSpec']) + if m.get('AlgorithmVersion') is not None: + self.algorithm_version = m.get('AlgorithmVersion') + if m.get('CodeDir') is not None: + temp_model = Location() + self.code_dir = temp_model.from_map(m['CodeDir']) + if m.get('ComputeResource') is not None: + temp_model = CreateTrainingJobRequestComputeResource() + self.compute_resource = temp_model.from_map(m['ComputeResource']) + if m.get('Environments') is not None: + self.environments = m.get('Environments') + if m.get('ExperimentConfig') is not None: + temp_model = CreateTrainingJobRequestExperimentConfig() + self.experiment_config = temp_model.from_map(m['ExperimentConfig']) + self.hyper_parameters = [] + if m.get('HyperParameters') is not None: + for k in m.get('HyperParameters'): + temp_model = CreateTrainingJobRequestHyperParameters() + self.hyper_parameters.append(temp_model.from_map(k)) + self.input_channels = [] + if m.get('InputChannels') is not None: + for k in m.get('InputChannels'): + temp_model = CreateTrainingJobRequestInputChannels() + self.input_channels.append(temp_model.from_map(k)) + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = CreateTrainingJobRequestLabels() + self.labels.append(temp_model.from_map(k)) + self.output_channels = [] + if m.get('OutputChannels') is not None: + for k in m.get('OutputChannels'): + temp_model = CreateTrainingJobRequestOutputChannels() + self.output_channels.append(temp_model.from_map(k)) + if m.get('PythonRequirements') is not None: + self.python_requirements = m.get('PythonRequirements') + if m.get('RoleArn') is not None: + self.role_arn = m.get('RoleArn') + if m.get('Scheduler') is not None: + temp_model = CreateTrainingJobRequestScheduler() + self.scheduler = temp_model.from_map(m['Scheduler']) + if m.get('Settings') is not None: + temp_model = CreateTrainingJobRequestSettings() + self.settings = temp_model.from_map(m['Settings']) + if m.get('TrainingJobDescription') is not None: + self.training_job_description = m.get('TrainingJobDescription') + if m.get('TrainingJobName') is not None: + self.training_job_name = m.get('TrainingJobName') + if m.get('UserVpc') is not None: + temp_model = CreateTrainingJobRequestUserVpc() + self.user_vpc = temp_model.from_map(m['UserVpc']) + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class GetAlgorithmResponseBody(TeaModel): +class CreateTrainingJobResponseBody(TeaModel): def __init__( self, - algorithm_description: str = None, - algorithm_id: str = None, - algorithm_name: str = None, - algorithm_provider: str = None, - display_name: str = None, - gmt_create_time: str = None, - gmt_modified_time: str = None, request_id: str = None, - tenant_id: str = None, - user_id: str = None, - workspace_id: str = None, + training_job_id: str = None, ): - self.algorithm_description = algorithm_description - self.algorithm_id = algorithm_id - self.algorithm_name = algorithm_name - self.algorithm_provider = algorithm_provider - self.display_name = display_name - self.gmt_create_time = gmt_create_time - self.gmt_modified_time = gmt_modified_time self.request_id = request_id - self.tenant_id = tenant_id - self.user_id = user_id - self.workspace_id = workspace_id + self.training_job_id = training_job_id def validate(self): pass @@ -5695,72 +6613,33 @@ def to_map(self): return _map result = dict() - if self.algorithm_description is not None: - result['AlgorithmDescription'] = self.algorithm_description - if self.algorithm_id is not None: - result['AlgorithmId'] = self.algorithm_id - if self.algorithm_name is not None: - result['AlgorithmName'] = self.algorithm_name - if self.algorithm_provider is not None: - result['AlgorithmProvider'] = self.algorithm_provider - if self.display_name is not None: - result['DisplayName'] = self.display_name - if self.gmt_create_time is not None: - result['GmtCreateTime'] = self.gmt_create_time - if self.gmt_modified_time is not None: - result['GmtModifiedTime'] = self.gmt_modified_time if self.request_id is not None: result['RequestId'] = self.request_id - if self.tenant_id is not None: - result['TenantId'] = self.tenant_id - if self.user_id is not None: - result['UserId'] = self.user_id - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id + if self.training_job_id is not None: + result['TrainingJobId'] = self.training_job_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('AlgorithmDescription') is not None: - self.algorithm_description = m.get('AlgorithmDescription') - if m.get('AlgorithmId') is not None: - self.algorithm_id = m.get('AlgorithmId') - if m.get('AlgorithmName') is not None: - self.algorithm_name = m.get('AlgorithmName') - if m.get('AlgorithmProvider') is not None: - self.algorithm_provider = m.get('AlgorithmProvider') - if m.get('DisplayName') is not None: - self.display_name = m.get('DisplayName') - if m.get('GmtCreateTime') is not None: - self.gmt_create_time = m.get('GmtCreateTime') - if m.get('GmtModifiedTime') is not None: - self.gmt_modified_time = m.get('GmtModifiedTime') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('TenantId') is not None: - self.tenant_id = m.get('TenantId') - if m.get('UserId') is not None: - self.user_id = m.get('UserId') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') + if m.get('TrainingJobId') is not None: + self.training_job_id = m.get('TrainingJobId') return self -class GetAlgorithmResponse(TeaModel): +class CreateTrainingJobResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetAlgorithmResponseBody = None, + body: CreateTrainingJobResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -5785,37 +6664,20 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetAlgorithmResponseBody() + temp_model = CreateTrainingJobResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetAlgorithmVersionResponseBody(TeaModel): +class DeleteAlgorithmResponseBody(TeaModel): def __init__( self, - algorithm_id: str = None, - algorithm_name: str = None, - algorithm_provider: str = None, - algorithm_spec: AlgorithmSpec = None, - algorithm_version: str = None, - gmt_create_time: str = None, - gmt_modified_time: str = None, - tenant_id: str = None, - user_id: str = None, + request_id: str = None, ): - self.algorithm_id = algorithm_id - self.algorithm_name = algorithm_name - self.algorithm_provider = algorithm_provider - self.algorithm_spec = algorithm_spec - self.algorithm_version = algorithm_version - self.gmt_create_time = gmt_create_time - self.gmt_modified_time = gmt_modified_time - self.tenant_id = tenant_id - self.user_id = user_id + self.request_id = request_id def validate(self): - if self.algorithm_spec: - self.algorithm_spec.validate() + pass def to_map(self): _map = super().to_map() @@ -5823,65 +6685,29 @@ def to_map(self): return _map result = dict() - if self.algorithm_id is not None: - result['AlgorithmId'] = self.algorithm_id - if self.algorithm_name is not None: - result['AlgorithmName'] = self.algorithm_name - if self.algorithm_provider is not None: - result['AlgorithmProvider'] = self.algorithm_provider - if self.algorithm_spec is not None: - result['AlgorithmSpec'] = self.algorithm_spec.to_map() - if self.algorithm_version is not None: - result['AlgorithmVersion'] = self.algorithm_version - if self.gmt_create_time is not None: - result['GmtCreateTime'] = self.gmt_create_time - if self.gmt_modified_time is not None: - result['GmtModifiedTime'] = self.gmt_modified_time - if self.tenant_id is not None: - result['TenantId'] = self.tenant_id - if self.user_id is not None: - result['UserId'] = self.user_id + if self.request_id is not None: + result['requestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('AlgorithmId') is not None: - self.algorithm_id = m.get('AlgorithmId') - if m.get('AlgorithmName') is not None: - self.algorithm_name = m.get('AlgorithmName') - if m.get('AlgorithmProvider') is not None: - self.algorithm_provider = m.get('AlgorithmProvider') - if m.get('AlgorithmSpec') is not None: - temp_model = AlgorithmSpec() - self.algorithm_spec = temp_model.from_map(m['AlgorithmSpec']) - if m.get('AlgorithmVersion') is not None: - self.algorithm_version = m.get('AlgorithmVersion') - if m.get('GmtCreateTime') is not None: - self.gmt_create_time = m.get('GmtCreateTime') - if m.get('GmtModifiedTime') is not None: - self.gmt_modified_time = m.get('GmtModifiedTime') - if m.get('TenantId') is not None: - self.tenant_id = m.get('TenantId') - if m.get('UserId') is not None: - self.user_id = m.get('UserId') + if m.get('requestId') is not None: + self.request_id = m.get('requestId') return self -class GetAlgorithmVersionResponse(TeaModel): +class DeleteAlgorithmResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetAlgorithmVersionResponseBody = None, + body: DeleteAlgorithmResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -5906,23 +6732,17 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetAlgorithmVersionResponseBody() + temp_model = DeleteAlgorithmResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetComponentResponseBodyVersions(TeaModel): +class DeleteAlgorithmVersionResponseBody(TeaModel): def __init__( self, - gmt_create_time: str = None, - snapshot_id: str = None, - status: str = None, - version: str = None, + request_id: str = None, ): - self.gmt_create_time = gmt_create_time - self.snapshot_id = snapshot_id - self.status = status - self.version = version + self.request_id = request_id def validate(self): pass @@ -5933,69 +6753,67 @@ def to_map(self): return _map result = dict() - if self.gmt_create_time is not None: - result['GmtCreateTime'] = self.gmt_create_time - if self.snapshot_id is not None: - result['SnapshotId'] = self.snapshot_id - if self.status is not None: - result['Status'] = self.status - if self.version is not None: - result['Version'] = self.version + if self.request_id is not None: + result['RequestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('GmtCreateTime') is not None: - self.gmt_create_time = m.get('GmtCreateTime') - if m.get('SnapshotId') is not None: - self.snapshot_id = m.get('SnapshotId') - if m.get('Status') is not None: - self.status = m.get('Status') - if m.get('Version') is not None: - self.version = m.get('Version') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') return self -class GetComponentResponseBody(TeaModel): +class DeleteAlgorithmVersionResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: DeleteAlgorithmVersionResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = DeleteAlgorithmVersionResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class DeleteComponentResponseBody(TeaModel): def __init__( self, - component_id: str = None, - description: str = None, - display_name: str = None, - gmt_create_time: str = None, - gmt_modified_time: str = None, - labels: List[Label] = None, - name: str = None, - provider: str = None, request_id: str = None, - tenant_id: str = None, - user_id: str = None, - versions: List[GetComponentResponseBodyVersions] = None, - workspace_id: str = None, ): - self.component_id = component_id - self.description = description - self.display_name = display_name - self.gmt_create_time = gmt_create_time - self.gmt_modified_time = gmt_modified_time - self.labels = labels - self.name = name - self.provider = provider self.request_id = request_id - self.tenant_id = tenant_id - self.user_id = user_id - self.versions = versions - self.workspace_id = workspace_id def validate(self): - if self.labels: - for k in self.labels: - if k: - k.validate() - if self.versions: - for k in self.versions: - if k: - k.validate() + pass def to_map(self): _map = super().to_map() @@ -6003,90 +6821,29 @@ def to_map(self): return _map result = dict() - if self.component_id is not None: - result['ComponentId'] = self.component_id - if self.description is not None: - result['Description'] = self.description - if self.display_name is not None: - result['DisplayName'] = self.display_name - if self.gmt_create_time is not None: - result['GmtCreateTime'] = self.gmt_create_time - if self.gmt_modified_time is not None: - result['GmtModifiedTime'] = self.gmt_modified_time - result['Labels'] = [] - if self.labels is not None: - for k in self.labels: - result['Labels'].append(k.to_map() if k else None) - if self.name is not None: - result['Name'] = self.name - if self.provider is not None: - result['Provider'] = self.provider if self.request_id is not None: result['RequestId'] = self.request_id - if self.tenant_id is not None: - result['TenantId'] = self.tenant_id - if self.user_id is not None: - result['UserId'] = self.user_id - result['Versions'] = [] - if self.versions is not None: - for k in self.versions: - result['Versions'].append(k.to_map() if k else None) - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ComponentId') is not None: - self.component_id = m.get('ComponentId') - if m.get('Description') is not None: - self.description = m.get('Description') - if m.get('DisplayName') is not None: - self.display_name = m.get('DisplayName') - if m.get('GmtCreateTime') is not None: - self.gmt_create_time = m.get('GmtCreateTime') - if m.get('GmtModifiedTime') is not None: - self.gmt_modified_time = m.get('GmtModifiedTime') - self.labels = [] - if m.get('Labels') is not None: - for k in m.get('Labels'): - temp_model = Label() - self.labels.append(temp_model.from_map(k)) - if m.get('Name') is not None: - self.name = m.get('Name') - if m.get('Provider') is not None: - self.provider = m.get('Provider') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('TenantId') is not None: - self.tenant_id = m.get('TenantId') - if m.get('UserId') is not None: - self.user_id = m.get('UserId') - self.versions = [] - if m.get('Versions') is not None: - for k in m.get('Versions'): - temp_model = GetComponentResponseBodyVersions() - self.versions.append(temp_model.from_map(k)) - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') return self -class GetComponentResponse(TeaModel): +class DeleteComponentResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetComponentResponseBody = None, + body: DeleteComponentResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -6111,49 +6868,20 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetComponentResponseBody() + temp_model = DeleteComponentResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetComponentVersionResponseBody(TeaModel): +class DeleteComponentVersionResponseBody(TeaModel): def __init__( self, - description: str = None, - gmt_create_time: str = None, - gmt_modified_time: str = None, - labels: List[Label] = None, - name: str = None, - provider: str = None, request_id: str = None, - snapshot_id: str = None, - spec: ComponentSpec = None, - tenant_id: str = None, - user_id: str = None, - version: str = None, - workspace_id: str = None, ): - self.description = description - self.gmt_create_time = gmt_create_time - self.gmt_modified_time = gmt_modified_time - self.labels = labels - self.name = name - self.provider = provider self.request_id = request_id - self.snapshot_id = snapshot_id - self.spec = spec - self.tenant_id = tenant_id - self.user_id = user_id - self.version = version - self.workspace_id = workspace_id def validate(self): - if self.labels: - for k in self.labels: - if k: - k.validate() - if self.spec: - self.spec.validate() + pass def to_map(self): _map = super().to_map() @@ -6161,86 +6889,29 @@ def to_map(self): return _map result = dict() - if self.description is not None: - result['Description'] = self.description - if self.gmt_create_time is not None: - result['GmtCreateTime'] = self.gmt_create_time - if self.gmt_modified_time is not None: - result['GmtModifiedTime'] = self.gmt_modified_time - result['Labels'] = [] - if self.labels is not None: - for k in self.labels: - result['Labels'].append(k.to_map() if k else None) - if self.name is not None: - result['Name'] = self.name - if self.provider is not None: - result['Provider'] = self.provider if self.request_id is not None: result['RequestId'] = self.request_id - if self.snapshot_id is not None: - result['SnapshotId'] = self.snapshot_id - if self.spec is not None: - result['Spec'] = self.spec.to_map() - if self.tenant_id is not None: - result['TenantId'] = self.tenant_id - if self.user_id is not None: - result['UserId'] = self.user_id - if self.version is not None: - result['Version'] = self.version - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Description') is not None: - self.description = m.get('Description') - if m.get('GmtCreateTime') is not None: - self.gmt_create_time = m.get('GmtCreateTime') - if m.get('GmtModifiedTime') is not None: - self.gmt_modified_time = m.get('GmtModifiedTime') - self.labels = [] - if m.get('Labels') is not None: - for k in m.get('Labels'): - temp_model = Label() - self.labels.append(temp_model.from_map(k)) - if m.get('Name') is not None: - self.name = m.get('Name') - if m.get('Provider') is not None: - self.provider = m.get('Provider') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('SnapshotId') is not None: - self.snapshot_id = m.get('SnapshotId') - if m.get('Spec') is not None: - temp_model = ComponentSpec() - self.spec = temp_model.from_map(m['Spec']) - if m.get('TenantId') is not None: - self.tenant_id = m.get('TenantId') - if m.get('UserId') is not None: - self.user_id = m.get('UserId') - if m.get('Version') is not None: - self.version = m.get('Version') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') return self -class GetComponentVersionResponse(TeaModel): +class DeleteComponentVersionResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetComponentVersionResponseBody = None, + body: DeleteComponentVersionResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -6265,35 +6936,17 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetComponentVersionResponseBody() + temp_model = DeleteComponentVersionResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetComponentVersionSnapshotResponseBody(TeaModel): +class DeleteComponentVersionSnapshotResponseBody(TeaModel): def __init__( self, - component_id: str = None, - gmt_create_time: str = None, - gmt_modified_time: str = None, - is_current_version: bool = None, request_id: str = None, - snapshot_id: str = None, - tenant_id: str = None, - user_id: str = None, - version: str = None, - workspace_id: str = None, ): - self.component_id = component_id - self.gmt_create_time = gmt_create_time - self.gmt_modified_time = gmt_modified_time - self.is_current_version = is_current_version self.request_id = request_id - self.snapshot_id = snapshot_id - self.tenant_id = tenant_id - self.user_id = user_id - self.version = version - self.workspace_id = workspace_id def validate(self): pass @@ -6304,68 +6957,29 @@ def to_map(self): return _map result = dict() - if self.component_id is not None: - result['ComponentId'] = self.component_id - if self.gmt_create_time is not None: - result['GmtCreateTime'] = self.gmt_create_time - if self.gmt_modified_time is not None: - result['GmtModifiedTime'] = self.gmt_modified_time - if self.is_current_version is not None: - result['IsCurrentVersion'] = self.is_current_version if self.request_id is not None: result['RequestId'] = self.request_id - if self.snapshot_id is not None: - result['SnapshotId'] = self.snapshot_id - if self.tenant_id is not None: - result['TenantId'] = self.tenant_id - if self.user_id is not None: - result['UserId'] = self.user_id - if self.version is not None: - result['Version'] = self.version - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ComponentId') is not None: - self.component_id = m.get('ComponentId') - if m.get('GmtCreateTime') is not None: - self.gmt_create_time = m.get('GmtCreateTime') - if m.get('GmtModifiedTime') is not None: - self.gmt_modified_time = m.get('GmtModifiedTime') - if m.get('IsCurrentVersion') is not None: - self.is_current_version = m.get('IsCurrentVersion') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('SnapshotId') is not None: - self.snapshot_id = m.get('SnapshotId') - if m.get('TenantId') is not None: - self.tenant_id = m.get('TenantId') - if m.get('UserId') is not None: - self.user_id = m.get('UserId') - if m.get('Version') is not None: - self.version = m.get('Version') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') return self -class GetComponentVersionSnapshotResponse(TeaModel): +class DeleteComponentVersionSnapshotResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetComponentVersionSnapshotResponseBody = None, + body: DeleteComponentVersionSnapshotResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -6390,35 +7004,17 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetComponentVersionSnapshotResponseBody() + temp_model = DeleteComponentVersionSnapshotResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetInstanceJobResponseBody(TeaModel): +class DeleteLLMProjectResponseBody(TeaModel): def __init__( self, - creator: str = None, - gmt_create_time: str = None, - instance_id: str = None, - instance_job_id: str = None, - instance_job_type: str = None, - reason_code: str = None, - reason_message: str = None, request_id: str = None, - status: str = None, - workspace_id: str = None, ): - self.creator = creator - self.gmt_create_time = gmt_create_time - self.instance_id = instance_id - self.instance_job_id = instance_job_id - self.instance_job_type = instance_job_type - self.reason_code = reason_code - self.reason_message = reason_message self.request_id = request_id - self.status = status - self.workspace_id = workspace_id def validate(self): pass @@ -6429,68 +7025,29 @@ def to_map(self): return _map result = dict() - if self.creator is not None: - result['Creator'] = self.creator - if self.gmt_create_time is not None: - result['GmtCreateTime'] = self.gmt_create_time - if self.instance_id is not None: - result['InstanceId'] = self.instance_id - if self.instance_job_id is not None: - result['InstanceJobId'] = self.instance_job_id - if self.instance_job_type is not None: - result['InstanceJobType'] = self.instance_job_type - if self.reason_code is not None: - result['ReasonCode'] = self.reason_code - if self.reason_message is not None: - result['ReasonMessage'] = self.reason_message if self.request_id is not None: result['RequestId'] = self.request_id - if self.status is not None: - result['Status'] = self.status - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Creator') is not None: - self.creator = m.get('Creator') - if m.get('GmtCreateTime') is not None: - self.gmt_create_time = m.get('GmtCreateTime') - if m.get('InstanceId') is not None: - self.instance_id = m.get('InstanceId') - if m.get('InstanceJobId') is not None: - self.instance_job_id = m.get('InstanceJobId') - if m.get('InstanceJobType') is not None: - self.instance_job_type = m.get('InstanceJobType') - if m.get('ReasonCode') is not None: - self.reason_code = m.get('ReasonCode') - if m.get('ReasonMessage') is not None: - self.reason_message = m.get('ReasonMessage') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('Status') is not None: - self.status = m.get('Status') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') return self -class GetInstanceJobResponse(TeaModel): +class DeleteLLMProjectResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetInstanceJobResponseBody = None, + body: DeleteLLMProjectResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -6515,94 +7072,22 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetInstanceJobResponseBody() + temp_model = DeleteLLMProjectResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetJobViewMetricsRequest(TeaModel): - def __init__( - self, - end_time: str = None, - page_number: int = None, - page_size: int = None, - sort_by: str = None, - start_time: str = None, - time_step: str = None, - workspace_id: str = None, - ): - self.end_time = end_time - self.page_number = page_number - self.page_size = page_size - self.sort_by = sort_by - self.start_time = start_time - self.time_step = time_step - self.workspace_id = workspace_id - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.end_time is not None: - result['EndTime'] = self.end_time - if self.page_number is not None: - result['PageNumber'] = self.page_number - if self.page_size is not None: - result['PageSize'] = self.page_size - if self.sort_by is not None: - result['SortBy'] = self.sort_by - if self.start_time is not None: - result['StartTime'] = self.start_time - if self.time_step is not None: - result['TimeStep'] = self.time_step - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('EndTime') is not None: - self.end_time = m.get('EndTime') - if m.get('PageNumber') is not None: - self.page_number = m.get('PageNumber') - if m.get('PageSize') is not None: - self.page_size = m.get('PageSize') - if m.get('SortBy') is not None: - self.sort_by = m.get('SortBy') - if m.get('StartTime') is not None: - self.start_time = m.get('StartTime') - if m.get('TimeStep') is not None: - self.time_step = m.get('TimeStep') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') - return self - - -class GetJobViewMetricsResponseBody(TeaModel): +class DeleteMachineGroupResponseBody(TeaModel): def __init__( self, - job_metrics: List[JobViewMetric] = None, + machine_group_id: str = None, request_id: str = None, - summary: JobViewMetric = None, - total: int = None, ): - self.job_metrics = job_metrics + self.machine_group_id = machine_group_id self.request_id = request_id - self.summary = summary - self.total = total def validate(self): - if self.job_metrics: - for k in self.job_metrics: - if k: - k.validate() - if self.summary: - self.summary.validate() + pass def to_map(self): _map = super().to_map() @@ -6610,50 +7095,33 @@ def to_map(self): return _map result = dict() - result['JobMetrics'] = [] - if self.job_metrics is not None: - for k in self.job_metrics: - result['JobMetrics'].append(k.to_map() if k else None) + if self.machine_group_id is not None: + result['MachineGroupID'] = self.machine_group_id if self.request_id is not None: result['RequestId'] = self.request_id - if self.summary is not None: - result['Summary'] = self.summary.to_map() - if self.total is not None: - result['Total'] = self.total return result def from_map(self, m: dict = None): m = m or dict() - self.job_metrics = [] - if m.get('JobMetrics') is not None: - for k in m.get('JobMetrics'): - temp_model = JobViewMetric() - self.job_metrics.append(temp_model.from_map(k)) + if m.get('MachineGroupID') is not None: + self.machine_group_id = m.get('MachineGroupID') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('Summary') is not None: - temp_model = JobViewMetric() - self.summary = temp_model.from_map(m['Summary']) - if m.get('Total') is not None: - self.total = m.get('Total') return self -class GetJobViewMetricsResponse(TeaModel): +class DeleteMachineGroupResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetJobViewMetricsResponseBody = None, + body: DeleteMachineGroupResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -6678,21 +7146,20 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetJobViewMetricsResponseBody() + temp_model = DeleteMachineGroupResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetJobsStatisticsByQuotaRequest(TeaModel): +class DeleteQuotaResponseBody(TeaModel): def __init__( self, - end_time: str = None, - start_time: str = None, - workspace_id: str = None, + quota_id: str = None, + request_id: str = None, ): - self.end_time = end_time - self.start_time = start_time - self.workspace_id = workspace_id + # Quota Id + self.quota_id = quota_id + self.request_id = request_id def validate(self): pass @@ -6703,51 +7170,10 @@ def to_map(self): return _map result = dict() - if self.end_time is not None: - result['EndTime'] = self.end_time - if self.start_time is not None: - result['StartTime'] = self.start_time - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('EndTime') is not None: - self.end_time = m.get('EndTime') - if m.get('StartTime') is not None: - self.start_time = m.get('StartTime') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') - return self - - -class GetJobsStatisticsByQuotaResponseBody(TeaModel): - def __init__( - self, - quota_id: str = None, - request_id: str = None, - statistics: Dict[str, Any] = None, - ): - self.quota_id = quota_id - self.request_id = request_id - self.statistics = statistics - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.quota_id is not None: - result['QuotaId'] = self.quota_id - if self.request_id is not None: - result['RequestId'] = self.request_id - if self.statistics is not None: - result['Statistics'] = self.statistics + if self.quota_id is not None: + result['QuotaId'] = self.quota_id + if self.request_id is not None: + result['RequestId'] = self.request_id return result def from_map(self, m: dict = None): @@ -6756,26 +7182,21 @@ def from_map(self, m: dict = None): self.quota_id = m.get('QuotaId') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('Statistics') is not None: - self.statistics = m.get('Statistics') return self -class GetJobsStatisticsByQuotaResponse(TeaModel): +class DeleteQuotaResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetJobsStatisticsByQuotaResponseBody = None, + body: DeleteQuotaResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -6800,21 +7221,17 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetJobsStatisticsByQuotaResponseBody() + temp_model = DeleteQuotaResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetJobsStatisticsByResourceGroupRequest(TeaModel): +class DeleteQuotaLabelsRequest(TeaModel): def __init__( self, - end_time: str = None, - start_time: str = None, - workspace_id: str = None, + keys: str = None, ): - self.end_time = end_time - self.start_time = start_time - self.workspace_id = workspace_id + self.keys = keys def validate(self): pass @@ -6825,33 +7242,25 @@ def to_map(self): return _map result = dict() - if self.end_time is not None: - result['EndTime'] = self.end_time - if self.start_time is not None: - result['StartTime'] = self.start_time - if self.workspace_id is not None: - result['WorkspaceID'] = self.workspace_id + if self.keys is not None: + result['Keys'] = self.keys return result def from_map(self, m: dict = None): m = m or dict() - if m.get('EndTime') is not None: - self.end_time = m.get('EndTime') - if m.get('StartTime') is not None: - self.start_time = m.get('StartTime') - if m.get('WorkspaceID') is not None: - self.workspace_id = m.get('WorkspaceID') + if m.get('Keys') is not None: + self.keys = m.get('Keys') return self -class GetJobsStatisticsByResourceGroupResponseBody(TeaModel): +class DeleteQuotaLabelsResponseBody(TeaModel): def __init__( self, + quota_id: str = None, request_id: str = None, - statistics: Dict[str, Any] = None, ): + self.quota_id = quota_id self.request_id = request_id - self.statistics = statistics def validate(self): pass @@ -6862,36 +7271,33 @@ def to_map(self): return _map result = dict() + if self.quota_id is not None: + result['QuotaId'] = self.quota_id if self.request_id is not None: result['RequestId'] = self.request_id - if self.statistics is not None: - result['Statistics'] = self.statistics return result def from_map(self, m: dict = None): m = m or dict() + if m.get('QuotaId') is not None: + self.quota_id = m.get('QuotaId') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('Statistics') is not None: - self.statistics = m.get('Statistics') return self -class GetJobsStatisticsByResourceGroupResponse(TeaModel): +class DeleteQuotaLabelsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetJobsStatisticsByResourceGroupResponseBody = None, + body: DeleteQuotaLabelsResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -6916,49 +7322,19 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetJobsStatisticsByResourceGroupResponseBody() + temp_model = DeleteQuotaLabelsResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetMachineGroupResponseBody(TeaModel): +class DeleteResourceGroupResponseBody(TeaModel): def __init__( self, - count: int = None, - default_driver: str = None, - duration: str = None, - ecs_type: str = None, - gmt_created: str = None, - gmt_expired: str = None, - gmt_modified: str = None, - gmt_started: str = None, - machine_group_id: str = None, - order_id: str = None, - pairesource_id: str = None, - pay_type: str = None, - pricing_cycle: str = None, - region_id: str = None, request_id: str = None, - status: str = None, - supported_drivers: List[str] = None, + resource_group_id: str = None, ): - self.count = count - self.default_driver = default_driver - self.duration = duration - self.ecs_type = ecs_type - self.gmt_created = gmt_created - self.gmt_expired = gmt_expired - self.gmt_modified = gmt_modified - self.gmt_started = gmt_started - self.machine_group_id = machine_group_id - self.order_id = order_id - self.pairesource_id = pairesource_id - self.pay_type = pay_type - self.pricing_cycle = pricing_cycle - self.region_id = region_id self.request_id = request_id - self.status = status - self.supported_drivers = supported_drivers + self.resource_group_id = resource_group_id def validate(self): pass @@ -6969,96 +7345,33 @@ def to_map(self): return _map result = dict() - if self.count is not None: - result['Count'] = self.count - if self.default_driver is not None: - result['DefaultDriver'] = self.default_driver - if self.duration is not None: - result['Duration'] = self.duration - if self.ecs_type is not None: - result['EcsType'] = self.ecs_type - if self.gmt_created is not None: - result['GmtCreated'] = self.gmt_created - if self.gmt_expired is not None: - result['GmtExpired'] = self.gmt_expired - if self.gmt_modified is not None: - result['GmtModified'] = self.gmt_modified - if self.gmt_started is not None: - result['GmtStarted'] = self.gmt_started - if self.machine_group_id is not None: - result['MachineGroupID'] = self.machine_group_id - if self.order_id is not None: - result['OrderID'] = self.order_id - if self.pairesource_id is not None: - result['PAIResourceID'] = self.pairesource_id - if self.pay_type is not None: - result['PayType'] = self.pay_type - if self.pricing_cycle is not None: - result['PricingCycle'] = self.pricing_cycle - if self.region_id is not None: - result['RegionID'] = self.region_id if self.request_id is not None: result['RequestId'] = self.request_id - if self.status is not None: - result['Status'] = self.status - if self.supported_drivers is not None: - result['SupportedDrivers'] = self.supported_drivers + if self.resource_group_id is not None: + result['ResourceGroupID'] = self.resource_group_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Count') is not None: - self.count = m.get('Count') - if m.get('DefaultDriver') is not None: - self.default_driver = m.get('DefaultDriver') - if m.get('Duration') is not None: - self.duration = m.get('Duration') - if m.get('EcsType') is not None: - self.ecs_type = m.get('EcsType') - if m.get('GmtCreated') is not None: - self.gmt_created = m.get('GmtCreated') - if m.get('GmtExpired') is not None: - self.gmt_expired = m.get('GmtExpired') - if m.get('GmtModified') is not None: - self.gmt_modified = m.get('GmtModified') - if m.get('GmtStarted') is not None: - self.gmt_started = m.get('GmtStarted') - if m.get('MachineGroupID') is not None: - self.machine_group_id = m.get('MachineGroupID') - if m.get('OrderID') is not None: - self.order_id = m.get('OrderID') - if m.get('PAIResourceID') is not None: - self.pairesource_id = m.get('PAIResourceID') - if m.get('PayType') is not None: - self.pay_type = m.get('PayType') - if m.get('PricingCycle') is not None: - self.pricing_cycle = m.get('PricingCycle') - if m.get('RegionID') is not None: - self.region_id = m.get('RegionID') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('Status') is not None: - self.status = m.get('Status') - if m.get('SupportedDrivers') is not None: - self.supported_drivers = m.get('SupportedDrivers') + if m.get('ResourceGroupID') is not None: + self.resource_group_id = m.get('ResourceGroupID') return self -class GetMachineGroupResponse(TeaModel): +class DeleteResourceGroupResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetMachineGroupResponseBody = None, + body: DeleteResourceGroupResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -7083,25 +7396,19 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetMachineGroupResponseBody() + temp_model = DeleteResourceGroupResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetNodeMetricsRequest(TeaModel): +class DeleteResourceGroupMachineGroupResponseBody(TeaModel): def __init__( self, - end_time: str = None, - gputype: str = None, - start_time: str = None, - time_step: str = None, - verbose: bool = None, + machine_group_id: str = None, + request_id: str = None, ): - self.end_time = end_time - self.gputype = gputype - self.start_time = start_time - self.time_step = time_step - self.verbose = verbose + self.machine_group_id = machine_group_id + self.request_id = request_id def validate(self): pass @@ -7112,49 +7419,35 @@ def to_map(self): return _map result = dict() - if self.end_time is not None: - result['EndTime'] = self.end_time - if self.gputype is not None: - result['GPUType'] = self.gputype - if self.start_time is not None: - result['StartTime'] = self.start_time - if self.time_step is not None: - result['TimeStep'] = self.time_step - if self.verbose is not None: - result['Verbose'] = self.verbose + if self.machine_group_id is not None: + result['MachineGroupID'] = self.machine_group_id + if self.request_id is not None: + result['RequestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('EndTime') is not None: - self.end_time = m.get('EndTime') - if m.get('GPUType') is not None: - self.gputype = m.get('GPUType') - if m.get('StartTime') is not None: - self.start_time = m.get('StartTime') - if m.get('TimeStep') is not None: - self.time_step = m.get('TimeStep') - if m.get('Verbose') is not None: - self.verbose = m.get('Verbose') + if m.get('MachineGroupID') is not None: + self.machine_group_id = m.get('MachineGroupID') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') return self -class GetNodeMetricsResponseBody(TeaModel): +class DeleteResourceGroupMachineGroupResponse(TeaModel): def __init__( self, - metric_type: str = None, - nodes_metrics: List[NodeMetric] = None, - resource_group_id: str = None, + headers: Dict[str, str] = None, + status_code: int = None, + body: DeleteResourceGroupMachineGroupResponseBody = None, ): - self.metric_type = metric_type - self.nodes_metrics = nodes_metrics - self.resource_group_id = resource_group_id + self.headers = headers + self.status_code = status_code + self.body = body def validate(self): - if self.nodes_metrics: - for k in self.nodes_metrics: - if k: - k.validate() + if self.body: + self.body.validate() def to_map(self): _map = super().to_map() @@ -7162,45 +7455,65 @@ def to_map(self): return _map result = dict() - if self.metric_type is not None: - result['MetricType'] = self.metric_type - result['NodesMetrics'] = [] - if self.nodes_metrics is not None: - for k in self.nodes_metrics: - result['NodesMetrics'].append(k.to_map() if k else None) - if self.resource_group_id is not None: - result['ResourceGroupID'] = self.resource_group_id + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() return result def from_map(self, m: dict = None): m = m or dict() - if m.get('MetricType') is not None: - self.metric_type = m.get('MetricType') - self.nodes_metrics = [] - if m.get('NodesMetrics') is not None: - for k in m.get('NodesMetrics'): - temp_model = NodeMetric() - self.nodes_metrics.append(temp_model.from_map(k)) - if m.get('ResourceGroupID') is not None: - self.resource_group_id = m.get('ResourceGroupID') + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = DeleteResourceGroupMachineGroupResponseBody() + self.body = temp_model.from_map(m['body']) return self -class GetNodeMetricsResponse(TeaModel): +class DeleteTrainingJobResponseBody(TeaModel): + def __init__( + self, + request_id: str = None, + ): + self.request_id = request_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.request_id is not None: + result['RequestId'] = self.request_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + return self + + +class DeleteTrainingJobResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetNodeMetricsResponseBody = None, + body: DeleteTrainingJobResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -7225,25 +7538,17 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetNodeMetricsResponseBody() + temp_model = DeleteTrainingJobResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetNodeViewMetricsRequest(TeaModel): +class DeleteTrainingJobLabelsRequest(TeaModel): def __init__( self, - node_id: str = None, - page_number: int = None, - page_size: int = None, - time_step: str = None, - workspace_id: str = None, + keys: str = None, ): - self.node_id = node_id - self.page_number = page_number - self.page_size = page_size - self.time_step = time_step - self.workspace_id = workspace_id + self.keys = keys def validate(self): pass @@ -7254,47 +7559,26 @@ def to_map(self): return _map result = dict() - if self.node_id is not None: - result['NodeId'] = self.node_id - if self.page_number is not None: - result['PageNumber'] = self.page_number - if self.page_size is not None: - result['PageSize'] = self.page_size - if self.time_step is not None: - result['TimeStep'] = self.time_step - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id + if self.keys is not None: + result['Keys'] = self.keys return result def from_map(self, m: dict = None): m = m or dict() - if m.get('NodeId') is not None: - self.node_id = m.get('NodeId') - if m.get('PageNumber') is not None: - self.page_number = m.get('PageNumber') - if m.get('PageSize') is not None: - self.page_size = m.get('PageSize') - if m.get('TimeStep') is not None: - self.time_step = m.get('TimeStep') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') + if m.get('Keys') is not None: + self.keys = m.get('Keys') return self -class GetNodeViewMetricsResponseBody(TeaModel): +class DeleteTrainingJobLabelsResponseBody(TeaModel): def __init__( self, - node_metrics: List[NodeViewMetric] = None, - total: int = None, + request_id: str = None, ): - self.node_metrics = node_metrics - self.total = total + self.request_id = request_id def validate(self): - if self.node_metrics: - for k in self.node_metrics: - if k: - k.validate() + pass def to_map(self): _map = super().to_map() @@ -7302,41 +7586,29 @@ def to_map(self): return _map result = dict() - result['NodeMetrics'] = [] - if self.node_metrics is not None: - for k in self.node_metrics: - result['NodeMetrics'].append(k.to_map() if k else None) - if self.total is not None: - result['Total'] = self.total + if self.request_id is not None: + result['RequestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() - self.node_metrics = [] - if m.get('NodeMetrics') is not None: - for k in m.get('NodeMetrics'): - temp_model = NodeViewMetric() - self.node_metrics.append(temp_model.from_map(k)) - if m.get('Total') is not None: - self.total = m.get('Total') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') return self -class GetNodeViewMetricsResponse(TeaModel): +class DeleteTrainingJobLabelsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetNodeViewMetricsResponseBody = None, + body: DeleteTrainingJobLabelsResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -7361,45 +7633,21 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetNodeViewMetricsResponseBody() + temp_model = DeleteTrainingJobLabelsResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetOperationResponseBody(TeaModel): +class DeployLLMSnapshotRequestWorkloadContainer(TeaModel): def __init__( self, - creator_id: str = None, - gmt_created_time: str = None, - gmt_end_time: str = None, - gmt_modified_time: str = None, - gmt_start_time: str = None, - object_id: str = None, - object_type: str = None, - operation_description: str = None, - operation_id: str = None, - operation_spec_json: str = None, - operation_type: str = None, - reason_code: str = None, - reason_message: str = None, - request_id: str = None, - status: str = None, + image: str = None, + port: int = None, + user_command: str = None, ): - self.creator_id = creator_id - self.gmt_created_time = gmt_created_time - self.gmt_end_time = gmt_end_time - self.gmt_modified_time = gmt_modified_time - self.gmt_start_time = gmt_start_time - self.object_id = object_id - self.object_type = object_type - self.operation_description = operation_description - self.operation_id = operation_id - self.operation_spec_json = operation_spec_json - self.operation_type = operation_type - self.reason_code = reason_code - self.reason_message = reason_message - self.request_id = request_id - self.status = status + self.image = image + self.port = port + self.user_command = user_command def validate(self): pass @@ -7410,90 +7658,38 @@ def to_map(self): return _map result = dict() - if self.creator_id is not None: - result['CreatorId'] = self.creator_id - if self.gmt_created_time is not None: - result['GmtCreatedTime'] = self.gmt_created_time - if self.gmt_end_time is not None: - result['GmtEndTime'] = self.gmt_end_time - if self.gmt_modified_time is not None: - result['GmtModifiedTime'] = self.gmt_modified_time - if self.gmt_start_time is not None: - result['GmtStartTime'] = self.gmt_start_time - if self.object_id is not None: - result['ObjectId'] = self.object_id - if self.object_type is not None: - result['ObjectType'] = self.object_type - if self.operation_description is not None: - result['OperationDescription'] = self.operation_description - if self.operation_id is not None: - result['OperationId'] = self.operation_id - if self.operation_spec_json is not None: - result['OperationSpecJson'] = self.operation_spec_json - if self.operation_type is not None: - result['OperationType'] = self.operation_type - if self.reason_code is not None: - result['ReasonCode'] = self.reason_code - if self.reason_message is not None: - result['ReasonMessage'] = self.reason_message - if self.request_id is not None: - result['RequestId'] = self.request_id - if self.status is not None: - result['Status'] = self.status + if self.image is not None: + result['Image'] = self.image + if self.port is not None: + result['Port'] = self.port + if self.user_command is not None: + result['UserCommand'] = self.user_command return result def from_map(self, m: dict = None): m = m or dict() - if m.get('CreatorId') is not None: - self.creator_id = m.get('CreatorId') - if m.get('GmtCreatedTime') is not None: - self.gmt_created_time = m.get('GmtCreatedTime') - if m.get('GmtEndTime') is not None: - self.gmt_end_time = m.get('GmtEndTime') - if m.get('GmtModifiedTime') is not None: - self.gmt_modified_time = m.get('GmtModifiedTime') - if m.get('GmtStartTime') is not None: - self.gmt_start_time = m.get('GmtStartTime') - if m.get('ObjectId') is not None: - self.object_id = m.get('ObjectId') - if m.get('ObjectType') is not None: - self.object_type = m.get('ObjectType') - if m.get('OperationDescription') is not None: - self.operation_description = m.get('OperationDescription') - if m.get('OperationId') is not None: - self.operation_id = m.get('OperationId') - if m.get('OperationSpecJson') is not None: - self.operation_spec_json = m.get('OperationSpecJson') - if m.get('OperationType') is not None: - self.operation_type = m.get('OperationType') - if m.get('ReasonCode') is not None: - self.reason_code = m.get('ReasonCode') - if m.get('ReasonMessage') is not None: - self.reason_message = m.get('ReasonMessage') - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - if m.get('Status') is not None: - self.status = m.get('Status') + if m.get('Image') is not None: + self.image = m.get('Image') + if m.get('Port') is not None: + self.port = m.get('Port') + if m.get('UserCommand') is not None: + self.user_command = m.get('UserCommand') return self -class GetOperationResponse(TeaModel): +class DeployLLMSnapshotRequestWorkloadExtraConfig(TeaModel): def __init__( self, - headers: Dict[str, str] = None, - status_code: int = None, - body: GetOperationResponseBody = None, + enable_webservice: bool = None, + job_max_running_time_minutes: int = None, + third_party_lib_dir: str = None, ): - self.headers = headers - self.status_code = status_code - self.body = body + self.enable_webservice = enable_webservice + self.job_max_running_time_minutes = job_max_running_time_minutes + self.third_party_lib_dir = third_party_lib_dir def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') - if self.body: - self.body.validate() + pass def to_map(self): _map = super().to_map() @@ -7501,92 +7697,40 @@ def to_map(self): return _map result = dict() - if self.headers is not None: - result['headers'] = self.headers - if self.status_code is not None: - result['statusCode'] = self.status_code - if self.body is not None: - result['body'] = self.body.to_map() + if self.enable_webservice is not None: + result['EnableWebservice'] = self.enable_webservice + if self.job_max_running_time_minutes is not None: + result['JobMaxRunningTimeMinutes'] = self.job_max_running_time_minutes + if self.third_party_lib_dir is not None: + result['ThirdPartyLibDir'] = self.third_party_lib_dir return result def from_map(self, m: dict = None): m = m or dict() - if m.get('headers') is not None: - self.headers = m.get('headers') - if m.get('statusCode') is not None: - self.status_code = m.get('statusCode') - if m.get('body') is not None: - temp_model = GetOperationResponseBody() - self.body = temp_model.from_map(m['body']) + if m.get('EnableWebservice') is not None: + self.enable_webservice = m.get('EnableWebservice') + if m.get('JobMaxRunningTimeMinutes') is not None: + self.job_max_running_time_minutes = m.get('JobMaxRunningTimeMinutes') + if m.get('ThirdPartyLibDir') is not None: + self.third_party_lib_dir = m.get('ThirdPartyLibDir') return self -class GetQuotaResponseBody(TeaModel): +class DeployLLMSnapshotRequestWorkloadResourceSpecResourceConfig(TeaModel): def __init__( self, - allocate_strategy: str = None, - creator_id: str = None, - description: str = None, - gmt_created_time: str = None, - gmt_modified_time: str = None, - labels: List[Label] = None, - latest_operation_id: str = None, - min: AllocateStrategySpec = None, - parent_quota_id: str = None, - quota_config: QuotaConfig = None, - quota_details: QuotaDetails = None, - quota_id: str = None, - quota_name: str = None, - reason_code: str = None, - reason_message: str = None, - request_id: str = None, - resource_group_ids: List[str] = None, - resource_type: str = None, - status: str = None, - sub_quotas: List[QuotaIdName] = None, - workspaces: List[WorkspaceIdName] = None, + cpu: int = None, + gpu: int = None, + memory_in_gi_b: int = None, + resource_group: str = None, ): - self.allocate_strategy = allocate_strategy - self.creator_id = creator_id - self.description = description - self.gmt_created_time = gmt_created_time - self.gmt_modified_time = gmt_modified_time - self.labels = labels - self.latest_operation_id = latest_operation_id - self.min = min - self.parent_quota_id = parent_quota_id - self.quota_config = quota_config - self.quota_details = quota_details - self.quota_id = quota_id - self.quota_name = quota_name - self.reason_code = reason_code - self.reason_message = reason_message - self.request_id = request_id - self.resource_group_ids = resource_group_ids - self.resource_type = resource_type - self.status = status - self.sub_quotas = sub_quotas - self.workspaces = workspaces + self.cpu = cpu + self.gpu = gpu + self.memory_in_gi_b = memory_in_gi_b + self.resource_group = resource_group def validate(self): - if self.labels: - for k in self.labels: - if k: - k.validate() - if self.min: - self.min.validate() - if self.quota_config: - self.quota_config.validate() - if self.quota_details: - self.quota_details.validate() - if self.sub_quotas: - for k in self.sub_quotas: - if k: - k.validate() - if self.workspaces: - for k in self.workspaces: - if k: - k.validate() + pass def to_map(self): _map = super().to_map() @@ -7594,132 +7738,43 @@ def to_map(self): return _map result = dict() - if self.allocate_strategy is not None: - result['AllocateStrategy'] = self.allocate_strategy - if self.creator_id is not None: - result['CreatorId'] = self.creator_id - if self.description is not None: - result['Description'] = self.description - if self.gmt_created_time is not None: - result['GmtCreatedTime'] = self.gmt_created_time - if self.gmt_modified_time is not None: - result['GmtModifiedTime'] = self.gmt_modified_time - result['Labels'] = [] - if self.labels is not None: - for k in self.labels: - result['Labels'].append(k.to_map() if k else None) - if self.latest_operation_id is not None: - result['LatestOperationId'] = self.latest_operation_id - if self.min is not None: - result['Min'] = self.min.to_map() - if self.parent_quota_id is not None: - result['ParentQuotaId'] = self.parent_quota_id - if self.quota_config is not None: - result['QuotaConfig'] = self.quota_config.to_map() - if self.quota_details is not None: - result['QuotaDetails'] = self.quota_details.to_map() - if self.quota_id is not None: - result['QuotaId'] = self.quota_id - if self.quota_name is not None: - result['QuotaName'] = self.quota_name - if self.reason_code is not None: - result['ReasonCode'] = self.reason_code - if self.reason_message is not None: - result['ReasonMessage'] = self.reason_message - if self.request_id is not None: - result['RequestId'] = self.request_id - if self.resource_group_ids is not None: - result['ResourceGroupIds'] = self.resource_group_ids - if self.resource_type is not None: - result['ResourceType'] = self.resource_type - if self.status is not None: - result['Status'] = self.status - result['SubQuotas'] = [] - if self.sub_quotas is not None: - for k in self.sub_quotas: - result['SubQuotas'].append(k.to_map() if k else None) - result['Workspaces'] = [] - if self.workspaces is not None: - for k in self.workspaces: - result['Workspaces'].append(k.to_map() if k else None) + if self.cpu is not None: + result['Cpu'] = self.cpu + if self.gpu is not None: + result['Gpu'] = self.gpu + if self.memory_in_gi_b is not None: + result['MemoryInGiB'] = self.memory_in_gi_b + if self.resource_group is not None: + result['ResourceGroup'] = self.resource_group return result def from_map(self, m: dict = None): m = m or dict() - if m.get('AllocateStrategy') is not None: - self.allocate_strategy = m.get('AllocateStrategy') - if m.get('CreatorId') is not None: - self.creator_id = m.get('CreatorId') - if m.get('Description') is not None: - self.description = m.get('Description') - if m.get('GmtCreatedTime') is not None: - self.gmt_created_time = m.get('GmtCreatedTime') - if m.get('GmtModifiedTime') is not None: - self.gmt_modified_time = m.get('GmtModifiedTime') - self.labels = [] - if m.get('Labels') is not None: - for k in m.get('Labels'): - temp_model = Label() - self.labels.append(temp_model.from_map(k)) - if m.get('LatestOperationId') is not None: - self.latest_operation_id = m.get('LatestOperationId') - if m.get('Min') is not None: - temp_model = AllocateStrategySpec() - self.min = temp_model.from_map(m['Min']) - if m.get('ParentQuotaId') is not None: - self.parent_quota_id = m.get('ParentQuotaId') - if m.get('QuotaConfig') is not None: - temp_model = QuotaConfig() - self.quota_config = temp_model.from_map(m['QuotaConfig']) - if m.get('QuotaDetails') is not None: - temp_model = QuotaDetails() - self.quota_details = temp_model.from_map(m['QuotaDetails']) - if m.get('QuotaId') is not None: - self.quota_id = m.get('QuotaId') - if m.get('QuotaName') is not None: - self.quota_name = m.get('QuotaName') - if m.get('ReasonCode') is not None: - self.reason_code = m.get('ReasonCode') - if m.get('ReasonMessage') is not None: - self.reason_message = m.get('ReasonMessage') - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - if m.get('ResourceGroupIds') is not None: - self.resource_group_ids = m.get('ResourceGroupIds') - if m.get('ResourceType') is not None: - self.resource_type = m.get('ResourceType') - if m.get('Status') is not None: - self.status = m.get('Status') - self.sub_quotas = [] - if m.get('SubQuotas') is not None: - for k in m.get('SubQuotas'): - temp_model = QuotaIdName() - self.sub_quotas.append(temp_model.from_map(k)) - self.workspaces = [] - if m.get('Workspaces') is not None: - for k in m.get('Workspaces'): - temp_model = WorkspaceIdName() - self.workspaces.append(temp_model.from_map(k)) + if m.get('Cpu') is not None: + self.cpu = m.get('Cpu') + if m.get('Gpu') is not None: + self.gpu = m.get('Gpu') + if m.get('MemoryInGiB') is not None: + self.memory_in_gi_b = m.get('MemoryInGiB') + if m.get('ResourceGroup') is not None: + self.resource_group = m.get('ResourceGroup') return self -class GetQuotaResponse(TeaModel): +class DeployLLMSnapshotRequestWorkloadResourceSpec(TeaModel): def __init__( self, - headers: Dict[str, str] = None, - status_code: int = None, - body: GetQuotaResponseBody = None, + ecs_spec: str = None, + instance_num: int = None, + resource_config: DeployLLMSnapshotRequestWorkloadResourceSpecResourceConfig = None, ): - self.headers = headers - self.status_code = status_code - self.body = body + self.ecs_spec = ecs_spec + self.instance_num = instance_num + self.resource_config = resource_config def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') - if self.body: - self.body.validate() + if self.resource_config: + self.resource_config.validate() def to_map(self): _map = super().to_map() @@ -7727,46 +7782,40 @@ def to_map(self): return _map result = dict() - if self.headers is not None: - result['headers'] = self.headers - if self.status_code is not None: - result['statusCode'] = self.status_code - if self.body is not None: - result['body'] = self.body.to_map() + if self.ecs_spec is not None: + result['EcsSpec'] = self.ecs_spec + if self.instance_num is not None: + result['InstanceNum'] = self.instance_num + if self.resource_config is not None: + result['ResourceConfig'] = self.resource_config.to_map() return result def from_map(self, m: dict = None): m = m or dict() - if m.get('headers') is not None: - self.headers = m.get('headers') - if m.get('statusCode') is not None: - self.status_code = m.get('statusCode') - if m.get('body') is not None: - temp_model = GetQuotaResponseBody() - self.body = temp_model.from_map(m['body']) + if m.get('EcsSpec') is not None: + self.ecs_spec = m.get('EcsSpec') + if m.get('InstanceNum') is not None: + self.instance_num = m.get('InstanceNum') + if m.get('ResourceConfig') is not None: + temp_model = DeployLLMSnapshotRequestWorkloadResourceSpecResourceConfig() + self.resource_config = temp_model.from_map(m['ResourceConfig']) return self -class GetQuotaJobViewMetricsRequest(TeaModel): +class DeployLLMSnapshotRequestWorkloadUserVpc(TeaModel): def __init__( self, - end_time: str = None, - order: str = None, - page_number: int = None, - page_size: int = None, - sort_by: str = None, - start_time: str = None, - time_step: str = None, - workspace_id: str = None, + default_route: str = None, + extended_cidrs: List[str] = None, + security_group_id: str = None, + switch_id: str = None, + vpc_id: str = None, ): - self.end_time = end_time - self.order = order - self.page_number = page_number - self.page_size = page_size - self.sort_by = sort_by - self.start_time = start_time - self.time_step = time_step - self.workspace_id = workspace_id + self.default_route = default_route + self.extended_cidrs = extended_cidrs + self.security_group_id = security_group_id + self.switch_id = switch_id + self.vpc_id = vpc_id def validate(self): pass @@ -7777,67 +7826,153 @@ def to_map(self): return _map result = dict() - if self.end_time is not None: - result['EndTime'] = self.end_time - if self.order is not None: - result['Order'] = self.order - if self.page_number is not None: - result['PageNumber'] = self.page_number - if self.page_size is not None: - result['PageSize'] = self.page_size - if self.sort_by is not None: - result['SortBy'] = self.sort_by - if self.start_time is not None: - result['StartTime'] = self.start_time - if self.time_step is not None: - result['TimeStep'] = self.time_step - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id + if self.default_route is not None: + result['DefaultRoute'] = self.default_route + if self.extended_cidrs is not None: + result['ExtendedCIDRs'] = self.extended_cidrs + if self.security_group_id is not None: + result['SecurityGroupId'] = self.security_group_id + if self.switch_id is not None: + result['SwitchId'] = self.switch_id + if self.vpc_id is not None: + result['VpcId'] = self.vpc_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('EndTime') is not None: - self.end_time = m.get('EndTime') - if m.get('Order') is not None: - self.order = m.get('Order') - if m.get('PageNumber') is not None: - self.page_number = m.get('PageNumber') - if m.get('PageSize') is not None: - self.page_size = m.get('PageSize') - if m.get('SortBy') is not None: - self.sort_by = m.get('SortBy') - if m.get('StartTime') is not None: - self.start_time = m.get('StartTime') - if m.get('TimeStep') is not None: - self.time_step = m.get('TimeStep') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') + if m.get('DefaultRoute') is not None: + self.default_route = m.get('DefaultRoute') + if m.get('ExtendedCIDRs') is not None: + self.extended_cidrs = m.get('ExtendedCIDRs') + if m.get('SecurityGroupId') is not None: + self.security_group_id = m.get('SecurityGroupId') + if m.get('SwitchId') is not None: + self.switch_id = m.get('SwitchId') + if m.get('VpcId') is not None: + self.vpc_id = m.get('VpcId') return self -class GetQuotaJobViewMetricsResponseBody(TeaModel): +class DeployLLMSnapshotRequestWorkload(TeaModel): def __init__( self, - job_metrics: List[QuotaJobViewMetric] = None, - quota_id: str = None, + container: DeployLLMSnapshotRequestWorkloadContainer = None, + extra_config: DeployLLMSnapshotRequestWorkloadExtraConfig = None, + resource_spec: DeployLLMSnapshotRequestWorkloadResourceSpec = None, + user_vpc: DeployLLMSnapshotRequestWorkloadUserVpc = None, + ): + self.container = container + self.extra_config = extra_config + self.resource_spec = resource_spec + self.user_vpc = user_vpc + + def validate(self): + if self.container: + self.container.validate() + if self.extra_config: + self.extra_config.validate() + if self.resource_spec: + self.resource_spec.validate() + if self.user_vpc: + self.user_vpc.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.container is not None: + result['Container'] = self.container.to_map() + if self.extra_config is not None: + result['ExtraConfig'] = self.extra_config.to_map() + if self.resource_spec is not None: + result['ResourceSpec'] = self.resource_spec.to_map() + if self.user_vpc is not None: + result['UserVpc'] = self.user_vpc.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Container') is not None: + temp_model = DeployLLMSnapshotRequestWorkloadContainer() + self.container = temp_model.from_map(m['Container']) + if m.get('ExtraConfig') is not None: + temp_model = DeployLLMSnapshotRequestWorkloadExtraConfig() + self.extra_config = temp_model.from_map(m['ExtraConfig']) + if m.get('ResourceSpec') is not None: + temp_model = DeployLLMSnapshotRequestWorkloadResourceSpec() + self.resource_spec = temp_model.from_map(m['ResourceSpec']) + if m.get('UserVpc') is not None: + temp_model = DeployLLMSnapshotRequestWorkloadUserVpc() + self.user_vpc = temp_model.from_map(m['UserVpc']) + return self + + +class DeployLLMSnapshotRequest(TeaModel): + def __init__( + self, + description: str = None, + display_name: str = None, + labels: Dict[str, Any] = None, + workload: DeployLLMSnapshotRequestWorkload = None, + ): + self.description = description + self.display_name = display_name + self.labels = labels + self.workload = workload + + def validate(self): + if self.workload: + self.workload.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.description is not None: + result['Description'] = self.description + if self.display_name is not None: + result['DisplayName'] = self.display_name + if self.labels is not None: + result['Labels'] = self.labels + if self.workload is not None: + result['Workload'] = self.workload.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Description') is not None: + self.description = m.get('Description') + if m.get('DisplayName') is not None: + self.display_name = m.get('DisplayName') + if m.get('Labels') is not None: + self.labels = m.get('Labels') + if m.get('Workload') is not None: + temp_model = DeployLLMSnapshotRequestWorkload() + self.workload = temp_model.from_map(m['Workload']) + return self + + +class DeployLLMSnapshotResponseBody(TeaModel): + def __init__( + self, + job_id: str = None, + job_name: str = None, + job_request_id: str = None, request_id: str = None, - summary: QuotaJobViewMetric = None, - total_count: int = None, + status: str = None, ): - self.job_metrics = job_metrics - self.quota_id = quota_id + self.job_id = job_id + self.job_name = job_name + self.job_request_id = job_request_id self.request_id = request_id - self.summary = summary - self.total_count = total_count + self.status = status def validate(self): - if self.job_metrics: - for k in self.job_metrics: - if k: - k.validate() - if self.summary: - self.summary.validate() + pass def to_map(self): _map = super().to_map() @@ -7845,54 +7980,45 @@ def to_map(self): return _map result = dict() - result['JobMetrics'] = [] - if self.job_metrics is not None: - for k in self.job_metrics: - result['JobMetrics'].append(k.to_map() if k else None) - if self.quota_id is not None: - result['QuotaId'] = self.quota_id + if self.job_id is not None: + result['JobId'] = self.job_id + if self.job_name is not None: + result['JobName'] = self.job_name + if self.job_request_id is not None: + result['JobRequestId'] = self.job_request_id if self.request_id is not None: result['RequestId'] = self.request_id - if self.summary is not None: - result['Summary'] = self.summary.to_map() - if self.total_count is not None: - result['TotalCount'] = self.total_count + if self.status is not None: + result['Status'] = self.status return result def from_map(self, m: dict = None): m = m or dict() - self.job_metrics = [] - if m.get('JobMetrics') is not None: - for k in m.get('JobMetrics'): - temp_model = QuotaJobViewMetric() - self.job_metrics.append(temp_model.from_map(k)) - if m.get('QuotaId') is not None: - self.quota_id = m.get('QuotaId') + if m.get('JobId') is not None: + self.job_id = m.get('JobId') + if m.get('JobName') is not None: + self.job_name = m.get('JobName') + if m.get('JobRequestId') is not None: + self.job_request_id = m.get('JobRequestId') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('Summary') is not None: - temp_model = QuotaJobViewMetric() - self.summary = temp_model.from_map(m['Summary']) - if m.get('TotalCount') is not None: - self.total_count = m.get('TotalCount') + if m.get('Status') is not None: + self.status = m.get('Status') return self -class GetQuotaJobViewMetricsResponse(TeaModel): +class DeployLLMSnapshotResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetQuotaJobViewMetricsResponseBody = None, + body: DeployLLMSnapshotResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -7917,72 +8043,26 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetQuotaJobViewMetricsResponseBody() + temp_model = DeployLLMSnapshotResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetQuotaMetricsRequest(TeaModel): - def __init__( - self, - end_time: str = None, - gputype: str = None, - start_time: str = None, - time_step: str = None, - ): - self.end_time = end_time - self.gputype = gputype - self.start_time = start_time - self.time_step = time_step - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.end_time is not None: - result['EndTime'] = self.end_time - if self.gputype is not None: - result['GPUType'] = self.gputype - if self.start_time is not None: - result['StartTime'] = self.start_time - if self.time_step is not None: - result['TimeStep'] = self.time_step - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('EndTime') is not None: - self.end_time = m.get('EndTime') - if m.get('GPUType') is not None: - self.gputype = m.get('GPUType') - if m.get('StartTime') is not None: - self.start_time = m.get('StartTime') - if m.get('TimeStep') is not None: - self.time_step = m.get('TimeStep') - return self - - -class GetQuotaMetricsResponseBody(TeaModel): +class GetAI4DDefaultBucketResponseBody(TeaModel): def __init__( self, - quota_id: str = None, - quota_metrics: List[QuotaMetric] = None, + extranet_endpoint: str = None, + intranet_endpoint: str = None, + name: str = None, request_id: str = None, ): - self.quota_id = quota_id - self.quota_metrics = quota_metrics + self.extranet_endpoint = extranet_endpoint + self.intranet_endpoint = intranet_endpoint + self.name = name self.request_id = request_id def validate(self): - if self.quota_metrics: - for k in self.quota_metrics: - if k: - k.validate() + pass def to_map(self): _map = super().to_map() @@ -7990,45 +8070,41 @@ def to_map(self): return _map result = dict() - if self.quota_id is not None: - result['QuotaId'] = self.quota_id - result['QuotaMetrics'] = [] - if self.quota_metrics is not None: - for k in self.quota_metrics: - result['QuotaMetrics'].append(k.to_map() if k else None) + if self.extranet_endpoint is not None: + result['ExtranetEndpoint'] = self.extranet_endpoint + if self.intranet_endpoint is not None: + result['IntranetEndpoint'] = self.intranet_endpoint + if self.name is not None: + result['Name'] = self.name if self.request_id is not None: result['RequestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('QuotaId') is not None: - self.quota_id = m.get('QuotaId') - self.quota_metrics = [] - if m.get('QuotaMetrics') is not None: - for k in m.get('QuotaMetrics'): - temp_model = QuotaMetric() - self.quota_metrics.append(temp_model.from_map(k)) + if m.get('ExtranetEndpoint') is not None: + self.extranet_endpoint = m.get('ExtranetEndpoint') + if m.get('IntranetEndpoint') is not None: + self.intranet_endpoint = m.get('IntranetEndpoint') + if m.get('Name') is not None: + self.name = m.get('Name') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') return self -class GetQuotaMetricsResponse(TeaModel): +class GetAI4DDefaultBucketResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetQuotaMetricsResponseBody = None, + body: GetAI4DDefaultBucketResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -8053,80 +8129,40 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetQuotaMetricsResponseBody() + temp_model = GetAI4DDefaultBucketResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetQuotaNodeMetricsRequest(TeaModel): +class GetAlgorithmResponseBody(TeaModel): def __init__( self, - end_time: str = None, - gputype: str = None, - start_time: str = None, - time_step: str = None, - verbose: bool = None, + algorithm_description: str = None, + algorithm_id: str = None, + algorithm_name: str = None, + algorithm_provider: str = None, + display_name: str = None, + gmt_create_time: str = None, + gmt_modified_time: str = None, + request_id: str = None, + tenant_id: str = None, + user_id: str = None, + workspace_id: str = None, ): - self.end_time = end_time - self.gputype = gputype - self.start_time = start_time - self.time_step = time_step - self.verbose = verbose - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.end_time is not None: - result['EndTime'] = self.end_time - if self.gputype is not None: - result['GPUType'] = self.gputype - if self.start_time is not None: - result['StartTime'] = self.start_time - if self.time_step is not None: - result['TimeStep'] = self.time_step - if self.verbose is not None: - result['Verbose'] = self.verbose - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('EndTime') is not None: - self.end_time = m.get('EndTime') - if m.get('GPUType') is not None: - self.gputype = m.get('GPUType') - if m.get('StartTime') is not None: - self.start_time = m.get('StartTime') - if m.get('TimeStep') is not None: - self.time_step = m.get('TimeStep') - if m.get('Verbose') is not None: - self.verbose = m.get('Verbose') - return self - - -class GetQuotaNodeMetricsResponseBody(TeaModel): - def __init__( - self, - metric_type: str = None, - nodes_metrics: List[NodeMetric] = None, - quota_id: str = None, - request_id: str = None, - ): - self.metric_type = metric_type - self.nodes_metrics = nodes_metrics - self.quota_id = quota_id + self.algorithm_description = algorithm_description + self.algorithm_id = algorithm_id + self.algorithm_name = algorithm_name + self.algorithm_provider = algorithm_provider + self.display_name = display_name + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time self.request_id = request_id + self.tenant_id = tenant_id + self.user_id = user_id + self.workspace_id = workspace_id def validate(self): - if self.nodes_metrics: - for k in self.nodes_metrics: - if k: - k.validate() + pass def to_map(self): _map = super().to_map() @@ -8134,49 +8170,69 @@ def to_map(self): return _map result = dict() - if self.metric_type is not None: - result['MetricType'] = self.metric_type - result['NodesMetrics'] = [] - if self.nodes_metrics is not None: - for k in self.nodes_metrics: - result['NodesMetrics'].append(k.to_map() if k else None) - if self.quota_id is not None: - result['QuotaId'] = self.quota_id + if self.algorithm_description is not None: + result['AlgorithmDescription'] = self.algorithm_description + if self.algorithm_id is not None: + result['AlgorithmId'] = self.algorithm_id + if self.algorithm_name is not None: + result['AlgorithmName'] = self.algorithm_name + if self.algorithm_provider is not None: + result['AlgorithmProvider'] = self.algorithm_provider + if self.display_name is not None: + result['DisplayName'] = self.display_name + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time if self.request_id is not None: result['RequestId'] = self.request_id + if self.tenant_id is not None: + result['TenantId'] = self.tenant_id + if self.user_id is not None: + result['UserId'] = self.user_id + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('MetricType') is not None: - self.metric_type = m.get('MetricType') - self.nodes_metrics = [] - if m.get('NodesMetrics') is not None: - for k in m.get('NodesMetrics'): - temp_model = NodeMetric() - self.nodes_metrics.append(temp_model.from_map(k)) - if m.get('QuotaId') is not None: - self.quota_id = m.get('QuotaId') + if m.get('AlgorithmDescription') is not None: + self.algorithm_description = m.get('AlgorithmDescription') + if m.get('AlgorithmId') is not None: + self.algorithm_id = m.get('AlgorithmId') + if m.get('AlgorithmName') is not None: + self.algorithm_name = m.get('AlgorithmName') + if m.get('AlgorithmProvider') is not None: + self.algorithm_provider = m.get('AlgorithmProvider') + if m.get('DisplayName') is not None: + self.display_name = m.get('DisplayName') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') + if m.get('TenantId') is not None: + self.tenant_id = m.get('TenantId') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class GetQuotaNodeMetricsResponse(TeaModel): +class GetAlgorithmResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetQuotaNodeMetricsResponseBody = None, + body: GetAlgorithmResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -8201,28 +8257,37 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetQuotaNodeMetricsResponseBody() + temp_model = GetAlgorithmResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetQuotaNodeViewMetricsRequest(TeaModel): +class GetAlgorithmVersionResponseBody(TeaModel): def __init__( self, - node_id: str = None, - page_number: int = None, - page_size: int = None, - time_step: str = None, - workspace_id: str = None, + algorithm_id: str = None, + algorithm_name: str = None, + algorithm_provider: str = None, + algorithm_spec: AlgorithmSpec = None, + algorithm_version: str = None, + gmt_create_time: str = None, + gmt_modified_time: str = None, + tenant_id: str = None, + user_id: str = None, ): - self.node_id = node_id - self.page_number = page_number - self.page_size = page_size - self.time_step = time_step - self.workspace_id = workspace_id + self.algorithm_id = algorithm_id + self.algorithm_name = algorithm_name + self.algorithm_provider = algorithm_provider + self.algorithm_spec = algorithm_spec + self.algorithm_version = algorithm_version + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time + self.tenant_id = tenant_id + self.user_id = user_id def validate(self): - pass + if self.algorithm_spec: + self.algorithm_spec.validate() def to_map(self): _map = super().to_map() @@ -8230,51 +8295,64 @@ def to_map(self): return _map result = dict() - if self.node_id is not None: - result['NodeId'] = self.node_id - if self.page_number is not None: - result['PageNumber'] = self.page_number - if self.page_size is not None: - result['PageSize'] = self.page_size - if self.time_step is not None: - result['TimeStep'] = self.time_step - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id + if self.algorithm_id is not None: + result['AlgorithmId'] = self.algorithm_id + if self.algorithm_name is not None: + result['AlgorithmName'] = self.algorithm_name + if self.algorithm_provider is not None: + result['AlgorithmProvider'] = self.algorithm_provider + if self.algorithm_spec is not None: + result['AlgorithmSpec'] = self.algorithm_spec.to_map() + if self.algorithm_version is not None: + result['AlgorithmVersion'] = self.algorithm_version + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + if self.tenant_id is not None: + result['TenantId'] = self.tenant_id + if self.user_id is not None: + result['UserId'] = self.user_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('NodeId') is not None: - self.node_id = m.get('NodeId') - if m.get('PageNumber') is not None: - self.page_number = m.get('PageNumber') - if m.get('PageSize') is not None: - self.page_size = m.get('PageSize') - if m.get('TimeStep') is not None: - self.time_step = m.get('TimeStep') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') + if m.get('AlgorithmId') is not None: + self.algorithm_id = m.get('AlgorithmId') + if m.get('AlgorithmName') is not None: + self.algorithm_name = m.get('AlgorithmName') + if m.get('AlgorithmProvider') is not None: + self.algorithm_provider = m.get('AlgorithmProvider') + if m.get('AlgorithmSpec') is not None: + temp_model = AlgorithmSpec() + self.algorithm_spec = temp_model.from_map(m['AlgorithmSpec']) + if m.get('AlgorithmVersion') is not None: + self.algorithm_version = m.get('AlgorithmVersion') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + if m.get('TenantId') is not None: + self.tenant_id = m.get('TenantId') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') return self -class GetQuotaNodeViewMetricsResponseBody(TeaModel): +class GetAlgorithmVersionResponse(TeaModel): def __init__( self, - node_metrics: List[NodeViewMetric] = None, - quota_id: str = None, - request_id: str = None, - total_count: int = None, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetAlgorithmVersionResponseBody = None, ): - self.node_metrics = node_metrics - self.quota_id = quota_id - self.request_id = request_id - self.total_count = total_count + self.headers = headers + self.status_code = status_code + self.body = body def validate(self): - if self.node_metrics: - for k in self.node_metrics: - if k: - k.validate() + if self.body: + self.body.validate() def to_map(self): _map = super().to_map() @@ -8282,64 +8360,12 @@ def to_map(self): return _map result = dict() - result['NodeMetrics'] = [] - if self.node_metrics is not None: - for k in self.node_metrics: - result['NodeMetrics'].append(k.to_map() if k else None) - if self.quota_id is not None: - result['QuotaId'] = self.quota_id - if self.request_id is not None: - result['RequestId'] = self.request_id - if self.total_count is not None: - result['TotalCount'] = self.total_count - return result - - def from_map(self, m: dict = None): - m = m or dict() - self.node_metrics = [] - if m.get('NodeMetrics') is not None: - for k in m.get('NodeMetrics'): - temp_model = NodeViewMetric() - self.node_metrics.append(temp_model.from_map(k)) - if m.get('QuotaId') is not None: - self.quota_id = m.get('QuotaId') - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - if m.get('TotalCount') is not None: - self.total_count = m.get('TotalCount') - return self - - -class GetQuotaNodeViewMetricsResponse(TeaModel): - def __init__( - self, - headers: Dict[str, str] = None, - status_code: int = None, - body: GetQuotaNodeViewMetricsResponseBody = None, - ): - self.headers = headers - self.status_code = status_code - self.body = body - - def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') - if self.body: - self.body.validate() - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.headers is not None: - result['headers'] = self.headers - if self.status_code is not None: - result['statusCode'] = self.status_code - if self.body is not None: - result['body'] = self.body.to_map() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() return result def from_map(self, m: dict = None): @@ -8349,31 +8375,23 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetQuotaNodeViewMetricsResponseBody() + temp_model = GetAlgorithmVersionResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetQuotaRangeUserViewMetricsRequest(TeaModel): +class GetComponentResponseBodyVersions(TeaModel): def __init__( self, - end_time: str = None, - order: str = None, - page_number: int = None, - page_size: int = None, - sort_by: str = None, - start_time: str = None, - user_id: str = None, - workspace_id: str = None, + gmt_create_time: str = None, + snapshot_id: str = None, + status: str = None, + version: str = None, ): - self.end_time = end_time - self.order = order - self.page_number = page_number - self.page_size = page_size - self.sort_by = sort_by - self.start_time = start_time - self.user_id = user_id - self.workspace_id = workspace_id + self.gmt_create_time = gmt_create_time + self.snapshot_id = snapshot_id + self.status = status + self.version = version def validate(self): pass @@ -8384,65 +8402,67 @@ def to_map(self): return _map result = dict() - if self.end_time is not None: - result['EndTime'] = self.end_time - if self.order is not None: - result['Order'] = self.order - if self.page_number is not None: - result['PageNumber'] = self.page_number - if self.page_size is not None: - result['PageSize'] = self.page_size - if self.sort_by is not None: - result['SortBy'] = self.sort_by - if self.start_time is not None: - result['StartTime'] = self.start_time - if self.user_id is not None: - result['UserId'] = self.user_id - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.snapshot_id is not None: + result['SnapshotId'] = self.snapshot_id + if self.status is not None: + result['Status'] = self.status + if self.version is not None: + result['Version'] = self.version return result def from_map(self, m: dict = None): m = m or dict() - if m.get('EndTime') is not None: - self.end_time = m.get('EndTime') - if m.get('Order') is not None: - self.order = m.get('Order') - if m.get('PageNumber') is not None: - self.page_number = m.get('PageNumber') - if m.get('PageSize') is not None: - self.page_size = m.get('PageSize') - if m.get('SortBy') is not None: - self.sort_by = m.get('SortBy') - if m.get('StartTime') is not None: - self.start_time = m.get('StartTime') - if m.get('UserId') is not None: - self.user_id = m.get('UserId') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('SnapshotId') is not None: + self.snapshot_id = m.get('SnapshotId') + if m.get('Status') is not None: + self.status = m.get('Status') + if m.get('Version') is not None: + self.version = m.get('Version') return self -class GetQuotaRangeUserViewMetricsResponseBody(TeaModel): +class GetComponentResponseBody(TeaModel): def __init__( self, - quota_id: str = None, + component_id: str = None, + description: str = None, + display_name: str = None, + gmt_create_time: str = None, + gmt_modified_time: str = None, + labels: List[Label] = None, + name: str = None, + provider: str = None, request_id: str = None, - summary: QuotaUserViewMetric = None, - total_count: int = None, - user_metrics: List[QuotaUserViewMetric] = None, + tenant_id: str = None, + user_id: str = None, + versions: List[GetComponentResponseBodyVersions] = None, + workspace_id: str = None, ): - self.quota_id = quota_id + self.component_id = component_id + self.description = description + self.display_name = display_name + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time + self.labels = labels + self.name = name + self.provider = provider self.request_id = request_id - self.summary = summary - self.total_count = total_count - self.user_metrics = user_metrics + self.tenant_id = tenant_id + self.user_id = user_id + self.versions = versions + self.workspace_id = workspace_id def validate(self): - if self.summary: - self.summary.validate() - if self.user_metrics: - for k in self.user_metrics: + if self.labels: + for k in self.labels: + if k: + k.validate() + if self.versions: + for k in self.versions: if k: k.validate() @@ -8452,54 +8472,87 @@ def to_map(self): return _map result = dict() - if self.quota_id is not None: - result['QuotaId'] = self.quota_id + if self.component_id is not None: + result['ComponentId'] = self.component_id + if self.description is not None: + result['Description'] = self.description + if self.display_name is not None: + result['DisplayName'] = self.display_name + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + if self.name is not None: + result['Name'] = self.name + if self.provider is not None: + result['Provider'] = self.provider if self.request_id is not None: result['RequestId'] = self.request_id - if self.summary is not None: - result['Summary'] = self.summary.to_map() - if self.total_count is not None: - result['TotalCount'] = self.total_count - result['UserMetrics'] = [] - if self.user_metrics is not None: - for k in self.user_metrics: - result['UserMetrics'].append(k.to_map() if k else None) + if self.tenant_id is not None: + result['TenantId'] = self.tenant_id + if self.user_id is not None: + result['UserId'] = self.user_id + result['Versions'] = [] + if self.versions is not None: + for k in self.versions: + result['Versions'].append(k.to_map() if k else None) + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('QuotaId') is not None: - self.quota_id = m.get('QuotaId') + if m.get('ComponentId') is not None: + self.component_id = m.get('ComponentId') + if m.get('Description') is not None: + self.description = m.get('Description') + if m.get('DisplayName') is not None: + self.display_name = m.get('DisplayName') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = Label() + self.labels.append(temp_model.from_map(k)) + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('Provider') is not None: + self.provider = m.get('Provider') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('Summary') is not None: - temp_model = QuotaUserViewMetric() - self.summary = temp_model.from_map(m['Summary']) - if m.get('TotalCount') is not None: - self.total_count = m.get('TotalCount') - self.user_metrics = [] - if m.get('UserMetrics') is not None: - for k in m.get('UserMetrics'): - temp_model = QuotaUserViewMetric() - self.user_metrics.append(temp_model.from_map(k)) + if m.get('TenantId') is not None: + self.tenant_id = m.get('TenantId') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + self.versions = [] + if m.get('Versions') is not None: + for k in m.get('Versions'): + temp_model = GetComponentResponseBodyVersions() + self.versions.append(temp_model.from_map(k)) + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class GetQuotaRangeUserViewMetricsResponse(TeaModel): +class GetComponentResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetQuotaRangeUserViewMetricsResponseBody = None, + body: GetComponentResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -8524,32 +8577,49 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetQuotaRangeUserViewMetricsResponseBody() + temp_model = GetComponentResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetQuotaUserViewMetricsRequest(TeaModel): +class GetComponentVersionResponseBody(TeaModel): def __init__( self, - order: str = None, - page_number: str = None, - page_size: str = None, - sort_by: str = None, - time_step: str = None, + description: str = None, + gmt_create_time: str = None, + gmt_modified_time: str = None, + labels: List[Label] = None, + name: str = None, + provider: str = None, + request_id: str = None, + snapshot_id: str = None, + spec: ComponentSpec = None, + tenant_id: str = None, user_id: str = None, + version: str = None, workspace_id: str = None, ): - self.order = order - self.page_number = page_number - self.page_size = page_size - self.sort_by = sort_by - self.time_step = time_step + self.description = description + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time + self.labels = labels + self.name = name + self.provider = provider + self.request_id = request_id + self.snapshot_id = snapshot_id + self.spec = spec + self.tenant_id = tenant_id self.user_id = user_id + self.version = version self.workspace_id = workspace_id def validate(self): - pass + if self.labels: + for k in self.labels: + if k: + k.validate() + if self.spec: + self.spec.validate() def to_map(self): _map = super().to_map() @@ -8557,63 +8627,139 @@ def to_map(self): return _map result = dict() - if self.order is not None: - result['Order'] = self.order - if self.page_number is not None: - result['PageNumber'] = self.page_number - if self.page_size is not None: - result['PageSize'] = self.page_size - if self.sort_by is not None: - result['SortBy'] = self.sort_by - if self.time_step is not None: - result['TimeStep'] = self.time_step + if self.description is not None: + result['Description'] = self.description + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + if self.name is not None: + result['Name'] = self.name + if self.provider is not None: + result['Provider'] = self.provider + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.snapshot_id is not None: + result['SnapshotId'] = self.snapshot_id + if self.spec is not None: + result['Spec'] = self.spec.to_map() + if self.tenant_id is not None: + result['TenantId'] = self.tenant_id if self.user_id is not None: result['UserId'] = self.user_id + if self.version is not None: + result['Version'] = self.version if self.workspace_id is not None: result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Order') is not None: - self.order = m.get('Order') - if m.get('PageNumber') is not None: - self.page_number = m.get('PageNumber') - if m.get('PageSize') is not None: - self.page_size = m.get('PageSize') - if m.get('SortBy') is not None: - self.sort_by = m.get('SortBy') - if m.get('TimeStep') is not None: - self.time_step = m.get('TimeStep') + if m.get('Description') is not None: + self.description = m.get('Description') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = Label() + self.labels.append(temp_model.from_map(k)) + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('Provider') is not None: + self.provider = m.get('Provider') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('SnapshotId') is not None: + self.snapshot_id = m.get('SnapshotId') + if m.get('Spec') is not None: + temp_model = ComponentSpec() + self.spec = temp_model.from_map(m['Spec']) + if m.get('TenantId') is not None: + self.tenant_id = m.get('TenantId') if m.get('UserId') is not None: self.user_id = m.get('UserId') + if m.get('Version') is not None: + self.version = m.get('Version') if m.get('WorkspaceId') is not None: self.workspace_id = m.get('WorkspaceId') return self -class GetQuotaUserViewMetricsResponseBody(TeaModel): +class GetComponentVersionResponse(TeaModel): def __init__( self, - quota_id: str = None, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetComponentVersionResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetComponentVersionResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetComponentVersionSnapshotResponseBody(TeaModel): + def __init__( + self, + component_id: str = None, + gmt_create_time: str = None, + gmt_modified_time: str = None, + is_current_version: bool = None, request_id: str = None, - summary: QuotaUserViewMetric = None, - total_count: int = None, - user_metrics: List[QuotaUserViewMetric] = None, + snapshot_id: str = None, + tenant_id: str = None, + user_id: str = None, + version: str = None, + workspace_id: str = None, ): - self.quota_id = quota_id + self.component_id = component_id + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time + self.is_current_version = is_current_version self.request_id = request_id - self.summary = summary - self.total_count = total_count - self.user_metrics = user_metrics + self.snapshot_id = snapshot_id + self.tenant_id = tenant_id + self.user_id = user_id + self.version = version + self.workspace_id = workspace_id def validate(self): - if self.summary: - self.summary.validate() - if self.user_metrics: - for k in self.user_metrics: - if k: - k.validate() + pass def to_map(self): _map = super().to_map() @@ -8621,54 +8767,65 @@ def to_map(self): return _map result = dict() - if self.quota_id is not None: - result['QuotaId'] = self.quota_id + if self.component_id is not None: + result['ComponentId'] = self.component_id + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + if self.is_current_version is not None: + result['IsCurrentVersion'] = self.is_current_version if self.request_id is not None: result['RequestId'] = self.request_id - if self.summary is not None: - result['Summary'] = self.summary.to_map() - if self.total_count is not None: - result['TotalCount'] = self.total_count - result['UserMetrics'] = [] - if self.user_metrics is not None: - for k in self.user_metrics: - result['UserMetrics'].append(k.to_map() if k else None) + if self.snapshot_id is not None: + result['SnapshotId'] = self.snapshot_id + if self.tenant_id is not None: + result['TenantId'] = self.tenant_id + if self.user_id is not None: + result['UserId'] = self.user_id + if self.version is not None: + result['Version'] = self.version + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('QuotaId') is not None: - self.quota_id = m.get('QuotaId') + if m.get('ComponentId') is not None: + self.component_id = m.get('ComponentId') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + if m.get('IsCurrentVersion') is not None: + self.is_current_version = m.get('IsCurrentVersion') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('Summary') is not None: - temp_model = QuotaUserViewMetric() - self.summary = temp_model.from_map(m['Summary']) - if m.get('TotalCount') is not None: - self.total_count = m.get('TotalCount') - self.user_metrics = [] - if m.get('UserMetrics') is not None: - for k in m.get('UserMetrics'): - temp_model = QuotaUserViewMetric() - self.user_metrics.append(temp_model.from_map(k)) + if m.get('SnapshotId') is not None: + self.snapshot_id = m.get('SnapshotId') + if m.get('TenantId') is not None: + self.tenant_id = m.get('TenantId') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + if m.get('Version') is not None: + self.version = m.get('Version') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class GetQuotaUserViewMetricsResponse(TeaModel): +class GetComponentVersionSnapshotResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetQuotaUserViewMetricsResponseBody = None, + body: GetComponentVersionSnapshotResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -8693,30 +8850,150 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetQuotaUserViewMetricsResponseBody() + temp_model = GetComponentVersionSnapshotResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetRangeUserViewMetricsRequest(TeaModel): +class GetInstanceJobResponseBody(TeaModel): def __init__( self, - end_time: str = None, - order: str = None, - page_number: int = None, - page_size: int = None, - sort_by: str = None, - start_time: str = None, - user_id: str = None, - workspace_id: str = None, + creator: str = None, + gmt_create_time: str = None, + instance_id: str = None, + instance_job_id: str = None, + instance_job_type: str = None, + reason_code: str = None, + reason_message: str = None, + request_id: str = None, + status: str = None, + workspace_id: str = None, + ): + self.creator = creator + self.gmt_create_time = gmt_create_time + self.instance_id = instance_id + self.instance_job_id = instance_job_id + self.instance_job_type = instance_job_type + self.reason_code = reason_code + self.reason_message = reason_message + self.request_id = request_id + self.status = status + self.workspace_id = workspace_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.creator is not None: + result['Creator'] = self.creator + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.instance_id is not None: + result['InstanceId'] = self.instance_id + if self.instance_job_id is not None: + result['InstanceJobId'] = self.instance_job_id + if self.instance_job_type is not None: + result['InstanceJobType'] = self.instance_job_type + if self.reason_code is not None: + result['ReasonCode'] = self.reason_code + if self.reason_message is not None: + result['ReasonMessage'] = self.reason_message + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.status is not None: + result['Status'] = self.status + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Creator') is not None: + self.creator = m.get('Creator') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('InstanceId') is not None: + self.instance_id = m.get('InstanceId') + if m.get('InstanceJobId') is not None: + self.instance_job_id = m.get('InstanceJobId') + if m.get('InstanceJobType') is not None: + self.instance_job_type = m.get('InstanceJobType') + if m.get('ReasonCode') is not None: + self.reason_code = m.get('ReasonCode') + if m.get('ReasonMessage') is not None: + self.reason_message = m.get('ReasonMessage') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('Status') is not None: + self.status = m.get('Status') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') + return self + + +class GetInstanceJobResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetInstanceJobResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetInstanceJobResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetJobViewMetricsRequest(TeaModel): + def __init__( + self, + end_time: str = None, + page_number: int = None, + page_size: int = None, + sort_by: str = None, + start_time: str = None, + time_step: str = None, + workspace_id: str = None, ): self.end_time = end_time - self.order = order self.page_number = page_number self.page_size = page_size self.sort_by = sort_by self.start_time = start_time - self.user_id = user_id + self.time_step = time_step self.workspace_id = workspace_id def validate(self): @@ -8730,8 +9007,6 @@ def to_map(self): result = dict() if self.end_time is not None: result['EndTime'] = self.end_time - if self.order is not None: - result['Order'] = self.order if self.page_number is not None: result['PageNumber'] = self.page_number if self.page_size is not None: @@ -8740,8 +9015,8 @@ def to_map(self): result['SortBy'] = self.sort_by if self.start_time is not None: result['StartTime'] = self.start_time - if self.user_id is not None: - result['UserId'] = self.user_id + if self.time_step is not None: + result['TimeStep'] = self.time_step if self.workspace_id is not None: result['WorkspaceId'] = self.workspace_id return result @@ -8750,8 +9025,6 @@ def from_map(self, m: dict = None): m = m or dict() if m.get('EndTime') is not None: self.end_time = m.get('EndTime') - if m.get('Order') is not None: - self.order = m.get('Order') if m.get('PageNumber') is not None: self.page_number = m.get('PageNumber') if m.get('PageSize') is not None: @@ -8760,31 +9033,33 @@ def from_map(self, m: dict = None): self.sort_by = m.get('SortBy') if m.get('StartTime') is not None: self.start_time = m.get('StartTime') - if m.get('UserId') is not None: - self.user_id = m.get('UserId') + if m.get('TimeStep') is not None: + self.time_step = m.get('TimeStep') if m.get('WorkspaceId') is not None: self.workspace_id = m.get('WorkspaceId') return self -class GetRangeUserViewMetricsResponseBody(TeaModel): +class GetJobViewMetricsResponseBody(TeaModel): def __init__( self, - summary: UserViewMetric = None, - user_metrics: List[UserViewMetric] = None, + job_metrics: List[JobViewMetric] = None, request_id: str = None, + summary: JobViewMetric = None, + total: int = None, ): - self.summary = summary - self.user_metrics = user_metrics + self.job_metrics = job_metrics self.request_id = request_id + self.summary = summary + self.total = total def validate(self): - if self.summary: - self.summary.validate() - if self.user_metrics: - for k in self.user_metrics: + if self.job_metrics: + for k in self.job_metrics: if k: k.validate() + if self.summary: + self.summary.validate() def to_map(self): _map = super().to_map() @@ -8792,46 +9067,47 @@ def to_map(self): return _map result = dict() + result['JobMetrics'] = [] + if self.job_metrics is not None: + for k in self.job_metrics: + result['JobMetrics'].append(k.to_map() if k else None) + if self.request_id is not None: + result['RequestId'] = self.request_id if self.summary is not None: result['Summary'] = self.summary.to_map() - result['UserMetrics'] = [] - if self.user_metrics is not None: - for k in self.user_metrics: - result['UserMetrics'].append(k.to_map() if k else None) - if self.request_id is not None: - result['requestId'] = self.request_id + if self.total is not None: + result['Total'] = self.total return result def from_map(self, m: dict = None): m = m or dict() + self.job_metrics = [] + if m.get('JobMetrics') is not None: + for k in m.get('JobMetrics'): + temp_model = JobViewMetric() + self.job_metrics.append(temp_model.from_map(k)) + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') if m.get('Summary') is not None: - temp_model = UserViewMetric() + temp_model = JobViewMetric() self.summary = temp_model.from_map(m['Summary']) - self.user_metrics = [] - if m.get('UserMetrics') is not None: - for k in m.get('UserMetrics'): - temp_model = UserViewMetric() - self.user_metrics.append(temp_model.from_map(k)) - if m.get('requestId') is not None: - self.request_id = m.get('requestId') + if m.get('Total') is not None: + self.total = m.get('Total') return self -class GetRangeUserViewMetricsResponse(TeaModel): +class GetJobViewMetricsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetRangeUserViewMetricsResponseBody = None, + body: GetJobViewMetricsResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -8856,17 +9132,21 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetRangeUserViewMetricsResponseBody() + temp_model = GetJobViewMetricsResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetResourceGroupRequest(TeaModel): +class GetJobsStatisticsByQuotaRequest(TeaModel): def __init__( self, - is_aiworkspace_data_enabled: bool = None, + end_time: str = None, + start_time: str = None, + workspace_id: str = None, ): - self.is_aiworkspace_data_enabled = is_aiworkspace_data_enabled + self.end_time = end_time + self.start_time = start_time + self.workspace_id = workspace_id def validate(self): pass @@ -8877,49 +9157,38 @@ def to_map(self): return _map result = dict() - if self.is_aiworkspace_data_enabled is not None: - result['IsAIWorkspaceDataEnabled'] = self.is_aiworkspace_data_enabled + if self.end_time is not None: + result['EndTime'] = self.end_time + if self.start_time is not None: + result['StartTime'] = self.start_time + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('IsAIWorkspaceDataEnabled') is not None: - self.is_aiworkspace_data_enabled = m.get('IsAIWorkspaceDataEnabled') + if m.get('EndTime') is not None: + self.end_time = m.get('EndTime') + if m.get('StartTime') is not None: + self.start_time = m.get('StartTime') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class GetResourceGroupResponseBody(TeaModel): +class GetJobsStatisticsByQuotaResponseBody(TeaModel): def __init__( self, - cluster_id: str = None, - computing_resource_provider: str = None, - creator_id: str = None, - gmt_created_time: str = None, - gmt_modified_time: str = None, - name: str = None, + quota_id: str = None, request_id: str = None, - resource_type: str = None, - status: str = None, - support_rdma: bool = None, - user_vpc: UserVpc = None, - workspace_id: str = None, + statistics: Dict[str, Any] = None, ): - self.cluster_id = cluster_id - self.computing_resource_provider = computing_resource_provider - self.creator_id = creator_id - self.gmt_created_time = gmt_created_time - self.gmt_modified_time = gmt_modified_time - self.name = name + self.quota_id = quota_id self.request_id = request_id - self.resource_type = resource_type - self.status = status - self.support_rdma = support_rdma - self.user_vpc = user_vpc - self.workspace_id = workspace_id + self.statistics = statistics def validate(self): - if self.user_vpc: - self.user_vpc.validate() + pass def to_map(self): _map = super().to_map() @@ -8927,77 +9196,37 @@ def to_map(self): return _map result = dict() - if self.cluster_id is not None: - result['ClusterID'] = self.cluster_id - if self.computing_resource_provider is not None: - result['ComputingResourceProvider'] = self.computing_resource_provider - if self.creator_id is not None: - result['CreatorID'] = self.creator_id - if self.gmt_created_time is not None: - result['GmtCreatedTime'] = self.gmt_created_time - if self.gmt_modified_time is not None: - result['GmtModifiedTime'] = self.gmt_modified_time - if self.name is not None: - result['Name'] = self.name + if self.quota_id is not None: + result['QuotaId'] = self.quota_id if self.request_id is not None: result['RequestId'] = self.request_id - if self.resource_type is not None: - result['ResourceType'] = self.resource_type - if self.status is not None: - result['Status'] = self.status - if self.support_rdma is not None: - result['SupportRDMA'] = self.support_rdma - if self.user_vpc is not None: - result['UserVpc'] = self.user_vpc.to_map() - if self.workspace_id is not None: - result['WorkspaceID'] = self.workspace_id + if self.statistics is not None: + result['Statistics'] = self.statistics return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ClusterID') is not None: - self.cluster_id = m.get('ClusterID') - if m.get('ComputingResourceProvider') is not None: - self.computing_resource_provider = m.get('ComputingResourceProvider') - if m.get('CreatorID') is not None: - self.creator_id = m.get('CreatorID') - if m.get('GmtCreatedTime') is not None: - self.gmt_created_time = m.get('GmtCreatedTime') - if m.get('GmtModifiedTime') is not None: - self.gmt_modified_time = m.get('GmtModifiedTime') - if m.get('Name') is not None: - self.name = m.get('Name') + if m.get('QuotaId') is not None: + self.quota_id = m.get('QuotaId') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('ResourceType') is not None: - self.resource_type = m.get('ResourceType') - if m.get('Status') is not None: - self.status = m.get('Status') - if m.get('SupportRDMA') is not None: - self.support_rdma = m.get('SupportRDMA') - if m.get('UserVpc') is not None: - temp_model = UserVpc() - self.user_vpc = temp_model.from_map(m['UserVpc']) - if m.get('WorkspaceID') is not None: - self.workspace_id = m.get('WorkspaceID') + if m.get('Statistics') is not None: + self.statistics = m.get('Statistics') return self -class GetResourceGroupResponse(TeaModel): +class GetJobsStatisticsByQuotaResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetResourceGroupResponseBody = None, + body: GetJobsStatisticsByQuotaResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -9022,53 +9251,58 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetResourceGroupResponseBody() + temp_model = GetJobsStatisticsByQuotaResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetResourceGroupMachineGroupResponseBody(TeaModel): +class GetJobsStatisticsByResourceGroupRequest(TeaModel): + def __init__( + self, + end_time: str = None, + start_time: str = None, + workspace_id: str = None, + ): + self.end_time = end_time + self.start_time = start_time + self.workspace_id = workspace_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.end_time is not None: + result['EndTime'] = self.end_time + if self.start_time is not None: + result['StartTime'] = self.start_time + if self.workspace_id is not None: + result['WorkspaceID'] = self.workspace_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('EndTime') is not None: + self.end_time = m.get('EndTime') + if m.get('StartTime') is not None: + self.start_time = m.get('StartTime') + if m.get('WorkspaceID') is not None: + self.workspace_id = m.get('WorkspaceID') + return self + + +class GetJobsStatisticsByResourceGroupResponseBody(TeaModel): def __init__( self, - cpu: str = None, - default_driver: str = None, - ecs_count: int = None, - ecs_spec: str = None, - gmt_created_time: str = None, - gmt_expired_time: str = None, - gmt_modified_time: str = None, - gmt_started_time: str = None, - gpu: str = None, - gpu_type: str = None, - machine_group_id: str = None, - memory: str = None, - payment_duration: str = None, - payment_duration_unit: str = None, - payment_type: str = None, request_id: str = None, - resource_group_id: str = None, - status: str = None, - supported_drivers: List[str] = None, + statistics: Dict[str, Any] = None, ): - self.cpu = cpu - self.default_driver = default_driver - self.ecs_count = ecs_count - self.ecs_spec = ecs_spec - self.gmt_created_time = gmt_created_time - self.gmt_expired_time = gmt_expired_time - self.gmt_modified_time = gmt_modified_time - self.gmt_started_time = gmt_started_time - self.gpu = gpu - self.gpu_type = gpu_type - self.machine_group_id = machine_group_id - self.memory = memory - self.payment_duration = payment_duration - self.payment_duration_unit = payment_duration_unit - self.payment_type = payment_type self.request_id = request_id - self.resource_group_id = resource_group_id - self.status = status - self.supported_drivers = supported_drivers + self.statistics = statistics def validate(self): pass @@ -9079,104 +9313,33 @@ def to_map(self): return _map result = dict() - if self.cpu is not None: - result['Cpu'] = self.cpu - if self.default_driver is not None: - result['DefaultDriver'] = self.default_driver - if self.ecs_count is not None: - result['EcsCount'] = self.ecs_count - if self.ecs_spec is not None: - result['EcsSpec'] = self.ecs_spec - if self.gmt_created_time is not None: - result['GmtCreatedTime'] = self.gmt_created_time - if self.gmt_expired_time is not None: - result['GmtExpiredTime'] = self.gmt_expired_time - if self.gmt_modified_time is not None: - result['GmtModifiedTime'] = self.gmt_modified_time - if self.gmt_started_time is not None: - result['GmtStartedTime'] = self.gmt_started_time - if self.gpu is not None: - result['Gpu'] = self.gpu - if self.gpu_type is not None: - result['GpuType'] = self.gpu_type - if self.machine_group_id is not None: - result['MachineGroupID'] = self.machine_group_id - if self.memory is not None: - result['Memory'] = self.memory - if self.payment_duration is not None: - result['PaymentDuration'] = self.payment_duration - if self.payment_duration_unit is not None: - result['PaymentDurationUnit'] = self.payment_duration_unit - if self.payment_type is not None: - result['PaymentType'] = self.payment_type if self.request_id is not None: result['RequestId'] = self.request_id - if self.resource_group_id is not None: - result['ResourceGroupID'] = self.resource_group_id - if self.status is not None: - result['Status'] = self.status - if self.supported_drivers is not None: - result['SupportedDrivers'] = self.supported_drivers + if self.statistics is not None: + result['Statistics'] = self.statistics return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Cpu') is not None: - self.cpu = m.get('Cpu') - if m.get('DefaultDriver') is not None: - self.default_driver = m.get('DefaultDriver') - if m.get('EcsCount') is not None: - self.ecs_count = m.get('EcsCount') - if m.get('EcsSpec') is not None: - self.ecs_spec = m.get('EcsSpec') - if m.get('GmtCreatedTime') is not None: - self.gmt_created_time = m.get('GmtCreatedTime') - if m.get('GmtExpiredTime') is not None: - self.gmt_expired_time = m.get('GmtExpiredTime') - if m.get('GmtModifiedTime') is not None: - self.gmt_modified_time = m.get('GmtModifiedTime') - if m.get('GmtStartedTime') is not None: - self.gmt_started_time = m.get('GmtStartedTime') - if m.get('Gpu') is not None: - self.gpu = m.get('Gpu') - if m.get('GpuType') is not None: - self.gpu_type = m.get('GpuType') - if m.get('MachineGroupID') is not None: - self.machine_group_id = m.get('MachineGroupID') - if m.get('Memory') is not None: - self.memory = m.get('Memory') - if m.get('PaymentDuration') is not None: - self.payment_duration = m.get('PaymentDuration') - if m.get('PaymentDurationUnit') is not None: - self.payment_duration_unit = m.get('PaymentDurationUnit') - if m.get('PaymentType') is not None: - self.payment_type = m.get('PaymentType') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('ResourceGroupID') is not None: - self.resource_group_id = m.get('ResourceGroupID') - if m.get('Status') is not None: - self.status = m.get('Status') - if m.get('SupportedDrivers') is not None: - self.supported_drivers = m.get('SupportedDrivers') + if m.get('Statistics') is not None: + self.statistics = m.get('Statistics') return self -class GetResourceGroupMachineGroupResponse(TeaModel): +class GetJobsStatisticsByResourceGroupResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetResourceGroupMachineGroupResponseBody = None, + body: GetJobsStatisticsByResourceGroupResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -9201,23 +9364,19 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetResourceGroupMachineGroupResponseBody() + temp_model = GetJobsStatisticsByResourceGroupResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetResourceGroupMetricsRequest(TeaModel): +class GetLLMProjectResponseBodyLabels(TeaModel): def __init__( self, - end_time: str = None, - gputype: str = None, - start_time: str = None, - time_step: str = None, + key: str = None, + value: str = None, ): - self.end_time = end_time - self.gputype = gputype - self.start_time = start_time - self.time_step = time_step + self.key = key + self.value = value def validate(self): pass @@ -9228,45 +9387,92 @@ def to_map(self): return _map result = dict() - if self.end_time is not None: - result['EndTime'] = self.end_time - if self.gputype is not None: - result['GPUType'] = self.gputype - if self.start_time is not None: - result['StartTime'] = self.start_time - if self.time_step is not None: - result['TimeStep'] = self.time_step + if self.key is not None: + result['Key'] = self.key + if self.value is not None: + result['Value'] = self.value return result def from_map(self, m: dict = None): m = m or dict() - if m.get('EndTime') is not None: - self.end_time = m.get('EndTime') - if m.get('GPUType') is not None: - self.gputype = m.get('GPUType') - if m.get('StartTime') is not None: - self.start_time = m.get('StartTime') - if m.get('TimeStep') is not None: - self.time_step = m.get('TimeStep') + if m.get('Key') is not None: + self.key = m.get('Key') + if m.get('Value') is not None: + self.value = m.get('Value') return self -class GetResourceGroupMetricsResponseBody(TeaModel): +class GetLLMProjectResponseBodyRuntime(TeaModel): + def __init__( + self, + runtime_id: str = None, + runtime_type: str = None, + ): + self.runtime_id = runtime_id + self.runtime_type = runtime_type + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.runtime_id is not None: + result['RuntimeId'] = self.runtime_id + if self.runtime_type is not None: + result['RuntimeType'] = self.runtime_type + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('RuntimeId') is not None: + self.runtime_id = m.get('RuntimeId') + if m.get('RuntimeType') is not None: + self.runtime_type = m.get('RuntimeType') + return self + + +class GetLLMProjectResponseBody(TeaModel): def __init__( self, + gmt_create_time: str = None, + gmt_modified_time: str = None, + labels: List[GetLLMProjectResponseBodyLabels] = None, + owner_id: str = None, + project_description: str = None, + project_id: str = None, + project_name: str = None, + project_type: str = None, request_id: str = None, - resource_group_id: str = None, - resource_group_metrics: List[ResourceGroupMetric] = None, + root_path: str = None, + runtime: GetLLMProjectResponseBodyRuntime = None, + user_id: str = None, + workspace_id: str = None, ): + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time + self.labels = labels + self.owner_id = owner_id + self.project_description = project_description + self.project_id = project_id + self.project_name = project_name + self.project_type = project_type self.request_id = request_id - self.resource_group_id = resource_group_id - self.resource_group_metrics = resource_group_metrics + self.root_path = root_path + self.runtime = runtime + self.user_id = user_id + self.workspace_id = workspace_id def validate(self): - if self.resource_group_metrics: - for k in self.resource_group_metrics: + if self.labels: + for k in self.labels: if k: k.validate() + if self.runtime: + self.runtime.validate() def to_map(self): _map = super().to_map() @@ -9274,45 +9480,83 @@ def to_map(self): return _map result = dict() + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + if self.owner_id is not None: + result['OwnerId'] = self.owner_id + if self.project_description is not None: + result['ProjectDescription'] = self.project_description + if self.project_id is not None: + result['ProjectId'] = self.project_id + if self.project_name is not None: + result['ProjectName'] = self.project_name + if self.project_type is not None: + result['ProjectType'] = self.project_type if self.request_id is not None: result['RequestId'] = self.request_id - if self.resource_group_id is not None: - result['ResourceGroupID'] = self.resource_group_id - result['ResourceGroupMetrics'] = [] - if self.resource_group_metrics is not None: - for k in self.resource_group_metrics: - result['ResourceGroupMetrics'].append(k.to_map() if k else None) + if self.root_path is not None: + result['RootPath'] = self.root_path + if self.runtime is not None: + result['Runtime'] = self.runtime.to_map() + if self.user_id is not None: + result['UserId'] = self.user_id + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = GetLLMProjectResponseBodyLabels() + self.labels.append(temp_model.from_map(k)) + if m.get('OwnerId') is not None: + self.owner_id = m.get('OwnerId') + if m.get('ProjectDescription') is not None: + self.project_description = m.get('ProjectDescription') + if m.get('ProjectId') is not None: + self.project_id = m.get('ProjectId') + if m.get('ProjectName') is not None: + self.project_name = m.get('ProjectName') + if m.get('ProjectType') is not None: + self.project_type = m.get('ProjectType') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('ResourceGroupID') is not None: - self.resource_group_id = m.get('ResourceGroupID') - self.resource_group_metrics = [] - if m.get('ResourceGroupMetrics') is not None: - for k in m.get('ResourceGroupMetrics'): - temp_model = ResourceGroupMetric() - self.resource_group_metrics.append(temp_model.from_map(k)) + if m.get('RootPath') is not None: + self.root_path = m.get('RootPath') + if m.get('Runtime') is not None: + temp_model = GetLLMProjectResponseBodyRuntime() + self.runtime = temp_model.from_map(m['Runtime']) + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class GetResourceGroupMetricsResponse(TeaModel): +class GetLLMProjectResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetResourceGroupMetricsResponseBody = None, + body: GetLLMProjectResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -9337,19 +9581,21 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetResourceGroupMetricsResponseBody() + temp_model = GetLLMProjectResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetResourceGroupRequestRequest(TeaModel): +class GetLLMServiceIdentityRoleResponseBody(TeaModel): def __init__( self, - pod_status: str = None, - resource_group_id: str = None, + exist: bool = None, + request_id: str = None, + role_name: str = None, ): - self.pod_status = pod_status - self.resource_group_id = resource_group_id + self.exist = exist + self.request_id = request_id + self.role_name = role_name def validate(self): pass @@ -9360,89 +9606,37 @@ def to_map(self): return _map result = dict() - if self.pod_status is not None: - result['PodStatus'] = self.pod_status - if self.resource_group_id is not None: - result['ResourceGroupID'] = self.resource_group_id - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('PodStatus') is not None: - self.pod_status = m.get('PodStatus') - if m.get('ResourceGroupID') is not None: - self.resource_group_id = m.get('ResourceGroupID') - return self - - -class GetResourceGroupRequestResponseBody(TeaModel): - def __init__( - self, - request_cpu: int = None, - request_gpu: int = None, - request_gpuinfos: List[GPUInfo] = None, - request_memory: int = None, - ): - self.request_cpu = request_cpu - self.request_gpu = request_gpu - self.request_gpuinfos = request_gpuinfos - self.request_memory = request_memory - - def validate(self): - if self.request_gpuinfos: - for k in self.request_gpuinfos: - if k: - k.validate() - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.request_cpu is not None: - result['requestCPU'] = self.request_cpu - if self.request_gpu is not None: - result['requestGPU'] = self.request_gpu - result['requestGPUInfos'] = [] - if self.request_gpuinfos is not None: - for k in self.request_gpuinfos: - result['requestGPUInfos'].append(k.to_map() if k else None) - if self.request_memory is not None: - result['requestMemory'] = self.request_memory + if self.exist is not None: + result['Exist'] = self.exist + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.role_name is not None: + result['RoleName'] = self.role_name return result def from_map(self, m: dict = None): m = m or dict() - if m.get('requestCPU') is not None: - self.request_cpu = m.get('requestCPU') - if m.get('requestGPU') is not None: - self.request_gpu = m.get('requestGPU') - self.request_gpuinfos = [] - if m.get('requestGPUInfos') is not None: - for k in m.get('requestGPUInfos'): - temp_model = GPUInfo() - self.request_gpuinfos.append(temp_model.from_map(k)) - if m.get('requestMemory') is not None: - self.request_memory = m.get('requestMemory') + if m.get('Exist') is not None: + self.exist = m.get('Exist') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('RoleName') is not None: + self.role_name = m.get('RoleName') return self -class GetResourceGroupRequestResponse(TeaModel): +class GetLLMServiceIdentityRoleResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetResourceGroupRequestResponseBody = None, + body: GetLLMServiceIdentityRoleResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -9467,17 +9661,19 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetResourceGroupRequestResponseBody() + temp_model = GetLLMServiceIdentityRoleResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetResourceGroupTotalRequest(TeaModel): +class GetLLMSnapshotResponseBodyContentStorage(TeaModel): def __init__( self, - resource_group_id: str = None, + location: str = None, + type: str = None, ): - self.resource_group_id = resource_group_id + self.location = location + self.type = type def validate(self): pass @@ -9488,35 +9684,45 @@ def to_map(self): return _map result = dict() - if self.resource_group_id is not None: - result['ResourceGroupID'] = self.resource_group_id + if self.location is not None: + result['Location'] = self.location + if self.type is not None: + result['Type'] = self.type return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ResourceGroupID') is not None: - self.resource_group_id = m.get('ResourceGroupID') + if m.get('Location') is not None: + self.location = m.get('Location') + if m.get('Type') is not None: + self.type = m.get('Type') return self -class GetResourceGroupTotalResponseBody(TeaModel): +class GetLLMSnapshotResponseBody(TeaModel): def __init__( self, - total_cpu: int = None, - total_gpu: int = None, - total_gpuinfos: List[GPUInfo] = None, - total_memory: int = None, + content_storage: GetLLMSnapshotResponseBodyContentStorage = None, + gmt_create_time: str = None, + gmt_modified_time: str = None, + owner_id: str = None, + project_id: str = None, + request_id: str = None, + snapshot_id: str = None, + user_id: str = None, ): - self.total_cpu = total_cpu - self.total_gpu = total_gpu - self.total_gpuinfos = total_gpuinfos - self.total_memory = total_memory + self.content_storage = content_storage + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time + self.owner_id = owner_id + self.project_id = project_id + self.request_id = request_id + self.snapshot_id = snapshot_id + self.user_id = user_id def validate(self): - if self.total_gpuinfos: - for k in self.total_gpuinfos: - if k: - k.validate() + if self.content_storage: + self.content_storage.validate() def to_map(self): _map = super().to_map() @@ -9524,49 +9730,58 @@ def to_map(self): return _map result = dict() - if self.total_cpu is not None: - result['totalCPU'] = self.total_cpu - if self.total_gpu is not None: - result['totalGPU'] = self.total_gpu - result['totalGPUInfos'] = [] - if self.total_gpuinfos is not None: - for k in self.total_gpuinfos: - result['totalGPUInfos'].append(k.to_map() if k else None) - if self.total_memory is not None: - result['totalMemory'] = self.total_memory + if self.content_storage is not None: + result['ContentStorage'] = self.content_storage.to_map() + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + if self.owner_id is not None: + result['OwnerId'] = self.owner_id + if self.project_id is not None: + result['ProjectId'] = self.project_id + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.snapshot_id is not None: + result['SnapshotId'] = self.snapshot_id + if self.user_id is not None: + result['UserId'] = self.user_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('totalCPU') is not None: - self.total_cpu = m.get('totalCPU') - if m.get('totalGPU') is not None: - self.total_gpu = m.get('totalGPU') - self.total_gpuinfos = [] - if m.get('totalGPUInfos') is not None: - for k in m.get('totalGPUInfos'): - temp_model = GPUInfo() - self.total_gpuinfos.append(temp_model.from_map(k)) - if m.get('totalMemory') is not None: - self.total_memory = m.get('totalMemory') + if m.get('ContentStorage') is not None: + temp_model = GetLLMSnapshotResponseBodyContentStorage() + self.content_storage = temp_model.from_map(m['ContentStorage']) + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + if m.get('OwnerId') is not None: + self.owner_id = m.get('OwnerId') + if m.get('ProjectId') is not None: + self.project_id = m.get('ProjectId') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('SnapshotId') is not None: + self.snapshot_id = m.get('SnapshotId') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') return self -class GetResourceGroupTotalResponse(TeaModel): +class GetLLMSnapshotResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetResourceGroupTotalResponseBody = None, + body: GetLLMSnapshotResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -9591,59 +9806,146 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetResourceGroupTotalResponseBody() + temp_model = GetLLMSnapshotResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetServiceIdentityRoleResponseBody(TeaModel): +class GetMachineGroupResponseBody(TeaModel): def __init__( self, - request_id: str = None, - role_name: str = None, - ): - self.request_id = request_id - self.role_name = role_name - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() + count: int = None, + default_driver: str = None, + duration: str = None, + ecs_type: str = None, + gmt_created: str = None, + gmt_expired: str = None, + gmt_modified: str = None, + gmt_started: str = None, + machine_group_id: str = None, + order_id: str = None, + pairesource_id: str = None, + pay_type: str = None, + pricing_cycle: str = None, + region_id: str = None, + request_id: str = None, + status: str = None, + supported_drivers: List[str] = None, + ): + self.count = count + self.default_driver = default_driver + self.duration = duration + self.ecs_type = ecs_type + self.gmt_created = gmt_created + self.gmt_expired = gmt_expired + self.gmt_modified = gmt_modified + self.gmt_started = gmt_started + self.machine_group_id = machine_group_id + self.order_id = order_id + self.pairesource_id = pairesource_id + self.pay_type = pay_type + self.pricing_cycle = pricing_cycle + self.region_id = region_id + self.request_id = request_id + self.status = status + self.supported_drivers = supported_drivers + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() if _map is not None: return _map result = dict() + if self.count is not None: + result['Count'] = self.count + if self.default_driver is not None: + result['DefaultDriver'] = self.default_driver + if self.duration is not None: + result['Duration'] = self.duration + if self.ecs_type is not None: + result['EcsType'] = self.ecs_type + if self.gmt_created is not None: + result['GmtCreated'] = self.gmt_created + if self.gmt_expired is not None: + result['GmtExpired'] = self.gmt_expired + if self.gmt_modified is not None: + result['GmtModified'] = self.gmt_modified + if self.gmt_started is not None: + result['GmtStarted'] = self.gmt_started + if self.machine_group_id is not None: + result['MachineGroupID'] = self.machine_group_id + if self.order_id is not None: + result['OrderID'] = self.order_id + if self.pairesource_id is not None: + result['PAIResourceID'] = self.pairesource_id + if self.pay_type is not None: + result['PayType'] = self.pay_type + if self.pricing_cycle is not None: + result['PricingCycle'] = self.pricing_cycle + if self.region_id is not None: + result['RegionID'] = self.region_id if self.request_id is not None: result['RequestId'] = self.request_id - if self.role_name is not None: - result['RoleName'] = self.role_name + if self.status is not None: + result['Status'] = self.status + if self.supported_drivers is not None: + result['SupportedDrivers'] = self.supported_drivers return result def from_map(self, m: dict = None): m = m or dict() + if m.get('Count') is not None: + self.count = m.get('Count') + if m.get('DefaultDriver') is not None: + self.default_driver = m.get('DefaultDriver') + if m.get('Duration') is not None: + self.duration = m.get('Duration') + if m.get('EcsType') is not None: + self.ecs_type = m.get('EcsType') + if m.get('GmtCreated') is not None: + self.gmt_created = m.get('GmtCreated') + if m.get('GmtExpired') is not None: + self.gmt_expired = m.get('GmtExpired') + if m.get('GmtModified') is not None: + self.gmt_modified = m.get('GmtModified') + if m.get('GmtStarted') is not None: + self.gmt_started = m.get('GmtStarted') + if m.get('MachineGroupID') is not None: + self.machine_group_id = m.get('MachineGroupID') + if m.get('OrderID') is not None: + self.order_id = m.get('OrderID') + if m.get('PAIResourceID') is not None: + self.pairesource_id = m.get('PAIResourceID') + if m.get('PayType') is not None: + self.pay_type = m.get('PayType') + if m.get('PricingCycle') is not None: + self.pricing_cycle = m.get('PricingCycle') + if m.get('RegionID') is not None: + self.region_id = m.get('RegionID') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('RoleName') is not None: - self.role_name = m.get('RoleName') + if m.get('Status') is not None: + self.status = m.get('Status') + if m.get('SupportedDrivers') is not None: + self.supported_drivers = m.get('SupportedDrivers') return self -class GetServiceIdentityRoleResponse(TeaModel): +class GetMachineGroupResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetServiceIdentityRoleResponseBody = None, + body: GetMachineGroupResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -9668,19 +9970,25 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetServiceIdentityRoleResponseBody() + temp_model = GetMachineGroupResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetTokenRequest(TeaModel): +class GetNodeMetricsRequest(TeaModel): def __init__( self, - expire_time: int = None, - training_job_id: str = None, + end_time: str = None, + gputype: str = None, + start_time: str = None, + time_step: str = None, + verbose: bool = None, ): - self.expire_time = expire_time - self.training_job_id = training_job_id + self.end_time = end_time + self.gputype = gputype + self.start_time = start_time + self.time_step = time_step + self.verbose = verbose def validate(self): pass @@ -9691,32 +9999,49 @@ def to_map(self): return _map result = dict() - if self.expire_time is not None: - result['ExpireTime'] = self.expire_time - if self.training_job_id is not None: - result['TrainingJobId'] = self.training_job_id + if self.end_time is not None: + result['EndTime'] = self.end_time + if self.gputype is not None: + result['GPUType'] = self.gputype + if self.start_time is not None: + result['StartTime'] = self.start_time + if self.time_step is not None: + result['TimeStep'] = self.time_step + if self.verbose is not None: + result['Verbose'] = self.verbose return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ExpireTime') is not None: - self.expire_time = m.get('ExpireTime') - if m.get('TrainingJobId') is not None: - self.training_job_id = m.get('TrainingJobId') + if m.get('EndTime') is not None: + self.end_time = m.get('EndTime') + if m.get('GPUType') is not None: + self.gputype = m.get('GPUType') + if m.get('StartTime') is not None: + self.start_time = m.get('StartTime') + if m.get('TimeStep') is not None: + self.time_step = m.get('TimeStep') + if m.get('Verbose') is not None: + self.verbose = m.get('Verbose') return self -class GetTokenResponseBody(TeaModel): +class GetNodeMetricsResponseBody(TeaModel): def __init__( self, - request_id: str = None, - token: str = None, + metric_type: str = None, + nodes_metrics: List[NodeMetric] = None, + resource_group_id: str = None, ): - self.request_id = request_id - self.token = token + self.metric_type = metric_type + self.nodes_metrics = nodes_metrics + self.resource_group_id = resource_group_id def validate(self): - pass + if self.nodes_metrics: + for k in self.nodes_metrics: + if k: + k.validate() def to_map(self): _map = super().to_map() @@ -9724,36 +10049,42 @@ def to_map(self): return _map result = dict() - if self.request_id is not None: - result['RequestId'] = self.request_id - if self.token is not None: - result['Token'] = self.token + if self.metric_type is not None: + result['MetricType'] = self.metric_type + result['NodesMetrics'] = [] + if self.nodes_metrics is not None: + for k in self.nodes_metrics: + result['NodesMetrics'].append(k.to_map() if k else None) + if self.resource_group_id is not None: + result['ResourceGroupID'] = self.resource_group_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - if m.get('Token') is not None: - self.token = m.get('Token') + if m.get('MetricType') is not None: + self.metric_type = m.get('MetricType') + self.nodes_metrics = [] + if m.get('NodesMetrics') is not None: + for k in m.get('NodesMetrics'): + temp_model = NodeMetric() + self.nodes_metrics.append(temp_model.from_map(k)) + if m.get('ResourceGroupID') is not None: + self.resource_group_id = m.get('ResourceGroupID') return self -class GetTokenResponse(TeaModel): +class GetNodeMetricsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetTokenResponseBody = None, + body: GetNodeMetricsResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -9778,17 +10109,25 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetTokenResponseBody() + temp_model = GetNodeMetricsResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetTrainingJobRequest(TeaModel): +class GetNodeViewMetricsRequest(TeaModel): def __init__( self, - token: str = None, + node_id: str = None, + page_number: int = None, + page_size: int = None, + time_step: str = None, + workspace_id: str = None, ): - self.token = token + self.node_id = node_id + self.page_number = page_number + self.page_size = page_size + self.time_step = time_step + self.workspace_id = workspace_id def validate(self): pass @@ -9799,61 +10138,47 @@ def to_map(self): return _map result = dict() - if self.token is not None: - result['Token'] = self.token + if self.node_id is not None: + result['NodeId'] = self.node_id + if self.page_number is not None: + result['PageNumber'] = self.page_number + if self.page_size is not None: + result['PageSize'] = self.page_size + if self.time_step is not None: + result['TimeStep'] = self.time_step + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Token') is not None: - self.token = m.get('Token') + if m.get('NodeId') is not None: + self.node_id = m.get('NodeId') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + if m.get('TimeStep') is not None: + self.time_step = m.get('TimeStep') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class GetTrainingJobResponseBodyComputeResource(TeaModel): +class GetNodeViewMetricsResponseBody(TeaModel): def __init__( self, - ecs_count: int = None, - ecs_spec: str = None, + node_metrics: List[NodeViewMetric] = None, + total: int = None, ): - self.ecs_count = ecs_count - self.ecs_spec = ecs_spec + self.node_metrics = node_metrics + self.total = total def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.ecs_count is not None: - result['EcsCount'] = self.ecs_count - if self.ecs_spec is not None: - result['EcsSpec'] = self.ecs_spec - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('EcsCount') is not None: - self.ecs_count = m.get('EcsCount') - if m.get('EcsSpec') is not None: - self.ecs_spec = m.get('EcsSpec') - return self - - -class GetTrainingJobResponseBodyHyperParameters(TeaModel): - def __init__( - self, - name: str = None, - value: str = None, - ): - self.name = name - self.value = value - - def validate(self): - pass + if self.node_metrics: + for k in self.node_metrics: + if k: + k.validate() def to_map(self): _map = super().to_map() @@ -9861,34 +10186,40 @@ def to_map(self): return _map result = dict() - if self.name is not None: - result['Name'] = self.name - if self.value is not None: - result['Value'] = self.value + result['NodeMetrics'] = [] + if self.node_metrics is not None: + for k in self.node_metrics: + result['NodeMetrics'].append(k.to_map() if k else None) + if self.total is not None: + result['Total'] = self.total return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Name') is not None: - self.name = m.get('Name') - if m.get('Value') is not None: - self.value = m.get('Value') + self.node_metrics = [] + if m.get('NodeMetrics') is not None: + for k in m.get('NodeMetrics'): + temp_model = NodeViewMetric() + self.node_metrics.append(temp_model.from_map(k)) + if m.get('Total') is not None: + self.total = m.get('Total') return self -class GetTrainingJobResponseBodyInputChannels(TeaModel): +class GetNodeViewMetricsResponse(TeaModel): def __init__( self, - dataset_id: str = None, - input_uri: str = None, - name: str = None, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetNodeViewMetricsResponseBody = None, ): - self.dataset_id = dataset_id - self.input_uri = input_uri - self.name = name + self.headers = headers + self.status_code = status_code + self.body = body def validate(self): - pass + if self.body: + self.body.validate() def to_map(self): _map = super().to_map() @@ -9896,34 +10227,59 @@ def to_map(self): return _map result = dict() - if self.dataset_id is not None: - result['DatasetId'] = self.dataset_id - if self.input_uri is not None: - result['InputUri'] = self.input_uri - if self.name is not None: - result['Name'] = self.name + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() return result def from_map(self, m: dict = None): m = m or dict() - if m.get('DatasetId') is not None: - self.dataset_id = m.get('DatasetId') - if m.get('InputUri') is not None: - self.input_uri = m.get('InputUri') - if m.get('Name') is not None: - self.name = m.get('Name') + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetNodeViewMetricsResponseBody() + self.body = temp_model.from_map(m['body']) return self -class GetTrainingJobResponseBodyInstances(TeaModel): +class GetOperationResponseBody(TeaModel): def __init__( self, - name: str = None, - role: str = None, + creator_id: str = None, + gmt_created_time: str = None, + gmt_end_time: str = None, + gmt_modified_time: str = None, + gmt_start_time: str = None, + object_id: str = None, + object_type: str = None, + operation_description: str = None, + operation_id: str = None, + operation_spec_json: str = None, + operation_type: str = None, + reason_code: str = None, + reason_message: str = None, + request_id: str = None, status: str = None, ): - self.name = name - self.role = role + self.creator_id = creator_id + self.gmt_created_time = gmt_created_time + self.gmt_end_time = gmt_end_time + self.gmt_modified_time = gmt_modified_time + self.gmt_start_time = gmt_start_time + self.object_id = object_id + self.object_type = object_type + self.operation_description = operation_description + self.operation_id = operation_id + self.operation_spec_json = operation_spec_json + self.operation_type = operation_type + self.reason_code = reason_code + self.reason_message = reason_message + self.request_id = request_id self.status = status def validate(self): @@ -9935,36 +10291,87 @@ def to_map(self): return _map result = dict() - if self.name is not None: - result['Name'] = self.name - if self.role is not None: - result['Role'] = self.role + if self.creator_id is not None: + result['CreatorId'] = self.creator_id + if self.gmt_created_time is not None: + result['GmtCreatedTime'] = self.gmt_created_time + if self.gmt_end_time is not None: + result['GmtEndTime'] = self.gmt_end_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + if self.gmt_start_time is not None: + result['GmtStartTime'] = self.gmt_start_time + if self.object_id is not None: + result['ObjectId'] = self.object_id + if self.object_type is not None: + result['ObjectType'] = self.object_type + if self.operation_description is not None: + result['OperationDescription'] = self.operation_description + if self.operation_id is not None: + result['OperationId'] = self.operation_id + if self.operation_spec_json is not None: + result['OperationSpecJson'] = self.operation_spec_json + if self.operation_type is not None: + result['OperationType'] = self.operation_type + if self.reason_code is not None: + result['ReasonCode'] = self.reason_code + if self.reason_message is not None: + result['ReasonMessage'] = self.reason_message + if self.request_id is not None: + result['RequestId'] = self.request_id if self.status is not None: result['Status'] = self.status return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Name') is not None: - self.name = m.get('Name') - if m.get('Role') is not None: - self.role = m.get('Role') + if m.get('CreatorId') is not None: + self.creator_id = m.get('CreatorId') + if m.get('GmtCreatedTime') is not None: + self.gmt_created_time = m.get('GmtCreatedTime') + if m.get('GmtEndTime') is not None: + self.gmt_end_time = m.get('GmtEndTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + if m.get('GmtStartTime') is not None: + self.gmt_start_time = m.get('GmtStartTime') + if m.get('ObjectId') is not None: + self.object_id = m.get('ObjectId') + if m.get('ObjectType') is not None: + self.object_type = m.get('ObjectType') + if m.get('OperationDescription') is not None: + self.operation_description = m.get('OperationDescription') + if m.get('OperationId') is not None: + self.operation_id = m.get('OperationId') + if m.get('OperationSpecJson') is not None: + self.operation_spec_json = m.get('OperationSpecJson') + if m.get('OperationType') is not None: + self.operation_type = m.get('OperationType') + if m.get('ReasonCode') is not None: + self.reason_code = m.get('ReasonCode') + if m.get('ReasonMessage') is not None: + self.reason_message = m.get('ReasonMessage') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') if m.get('Status') is not None: self.status = m.get('Status') return self -class GetTrainingJobResponseBodyLabels(TeaModel): +class GetOperationResponse(TeaModel): def __init__( self, - key: str = None, - value: str = None, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetOperationResponseBody = None, ): - self.key = key - self.value = value + self.headers = headers + self.status_code = status_code + self.body = body def validate(self): - pass + if self.body: + self.body.validate() def to_map(self): _map = super().to_map() @@ -9972,31 +10379,46 @@ def to_map(self): return _map result = dict() - if self.key is not None: - result['Key'] = self.key - if self.value is not None: - result['Value'] = self.value + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Key') is not None: - self.key = m.get('Key') - if m.get('Value') is not None: - self.value = m.get('Value') + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetOperationResponseBody() + self.body = temp_model.from_map(m['body']) return self -class GetTrainingJobResponseBodyLatestMetrics(TeaModel): +class GetQueueInfosRequest(TeaModel): def __init__( self, - name: str = None, - timestamp: str = None, - value: float = None, + order: str = None, + page_number: int = None, + page_size: int = None, + quota_ids: str = None, + sort_by: str = None, + workload_ids: str = None, + workload_type: str = None, + workspace_ids: str = None, ): - self.name = name - self.timestamp = timestamp - self.value = value + self.order = order + self.page_number = page_number + self.page_size = page_size + self.quota_ids = quota_ids + self.sort_by = sort_by + self.workload_ids = workload_ids + self.workload_type = workload_type + self.workspace_ids = workspace_ids def validate(self): pass @@ -10007,69 +10429,61 @@ def to_map(self): return _map result = dict() - if self.name is not None: - result['Name'] = self.name - if self.timestamp is not None: - result['Timestamp'] = self.timestamp - if self.value is not None: - result['Value'] = self.value - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('Name') is not None: - self.name = m.get('Name') - if m.get('Timestamp') is not None: - self.timestamp = m.get('Timestamp') - if m.get('Value') is not None: - self.value = m.get('Value') - return self - - -class GetTrainingJobResponseBodyLatestProgressOverallProgress(TeaModel): - def __init__( - self, - timestamp: str = None, - value: float = None, - ): - self.timestamp = timestamp - self.value = value - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.timestamp is not None: - result['Timestamp'] = self.timestamp - if self.value is not None: - result['Value'] = self.value + if self.order is not None: + result['Order'] = self.order + if self.page_number is not None: + result['PageNumber'] = self.page_number + if self.page_size is not None: + result['PageSize'] = self.page_size + if self.quota_ids is not None: + result['QuotaIds'] = self.quota_ids + if self.sort_by is not None: + result['SortBy'] = self.sort_by + if self.workload_ids is not None: + result['WorkloadIds'] = self.workload_ids + if self.workload_type is not None: + result['WorkloadType'] = self.workload_type + if self.workspace_ids is not None: + result['WorkspaceIds'] = self.workspace_ids return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Timestamp') is not None: - self.timestamp = m.get('Timestamp') - if m.get('Value') is not None: - self.value = m.get('Value') + if m.get('Order') is not None: + self.order = m.get('Order') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + if m.get('QuotaIds') is not None: + self.quota_ids = m.get('QuotaIds') + if m.get('SortBy') is not None: + self.sort_by = m.get('SortBy') + if m.get('WorkloadIds') is not None: + self.workload_ids = m.get('WorkloadIds') + if m.get('WorkloadType') is not None: + self.workload_type = m.get('WorkloadType') + if m.get('WorkspaceIds') is not None: + self.workspace_ids = m.get('WorkspaceIds') return self -class GetTrainingJobResponseBodyLatestProgressRemainingTime(TeaModel): +class GetQueueInfosResponseBody(TeaModel): def __init__( self, - timestamp: str = None, - value: int = None, + queue_infos: List[QueueInfo] = None, + request_id: str = None, + total_count: int = None, ): - self.timestamp = timestamp - self.value = value + self.queue_infos = queue_infos + self.request_id = request_id + self.total_count = total_count def validate(self): - pass + if self.queue_infos: + for k in self.queue_infos: + if k: + k.validate() def to_map(self): _map = super().to_map() @@ -10077,35 +10491,44 @@ def to_map(self): return _map result = dict() - if self.timestamp is not None: - result['Timestamp'] = self.timestamp - if self.value is not None: - result['Value'] = self.value + result['QueueInfos'] = [] + if self.queue_infos is not None: + for k in self.queue_infos: + result['QueueInfos'].append(k.to_map() if k else None) + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.total_count is not None: + result['TotalCount'] = self.total_count return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Timestamp') is not None: - self.timestamp = m.get('Timestamp') - if m.get('Value') is not None: - self.value = m.get('Value') + self.queue_infos = [] + if m.get('QueueInfos') is not None: + for k in m.get('QueueInfos'): + temp_model = QueueInfo() + self.queue_infos.append(temp_model.from_map(k)) + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') return self -class GetTrainingJobResponseBodyLatestProgress(TeaModel): +class GetQueueInfosResponse(TeaModel): def __init__( self, - overall_progress: GetTrainingJobResponseBodyLatestProgressOverallProgress = None, - remaining_time: GetTrainingJobResponseBodyLatestProgressRemainingTime = None, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetQueueInfosResponseBody = None, ): - self.overall_progress = overall_progress - self.remaining_time = remaining_time + self.headers = headers + self.status_code = status_code + self.body = body def validate(self): - if self.overall_progress: - self.overall_progress.validate() - if self.remaining_time: - self.remaining_time.validate() + if self.body: + self.body.validate() def to_map(self): _map = super().to_map() @@ -10113,36 +10536,95 @@ def to_map(self): return _map result = dict() - if self.overall_progress is not None: - result['OverallProgress'] = self.overall_progress.to_map() - if self.remaining_time is not None: - result['RemainingTime'] = self.remaining_time.to_map() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() return result def from_map(self, m: dict = None): m = m or dict() - if m.get('OverallProgress') is not None: - temp_model = GetTrainingJobResponseBodyLatestProgressOverallProgress() - self.overall_progress = temp_model.from_map(m['OverallProgress']) - if m.get('RemainingTime') is not None: - temp_model = GetTrainingJobResponseBodyLatestProgressRemainingTime() - self.remaining_time = temp_model.from_map(m['RemainingTime']) + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetQueueInfosResponseBody() + self.body = temp_model.from_map(m['body']) return self -class GetTrainingJobResponseBodyOutputChannels(TeaModel): +class GetQuotaResponseBody(TeaModel): def __init__( self, - dataset_id: str = None, - name: str = None, - output_uri: str = None, + allocate_strategy: str = None, + creator_id: str = None, + description: str = None, + gmt_created_time: str = None, + gmt_modified_time: str = None, + labels: List[Label] = None, + latest_operation_id: str = None, + min: ResourceSpec = None, + parent_quota_id: str = None, + queue_strategy: str = None, + quota_config: QuotaConfig = None, + quota_details: QuotaDetails = None, + quota_id: str = None, + quota_name: str = None, + reason_code: str = None, + reason_message: str = None, + request_id: str = None, + resource_group_ids: List[str] = None, + resource_type: str = None, + status: str = None, + sub_quotas: List[QuotaIdName] = None, + workspaces: List[WorkspaceIdName] = None, ): - self.dataset_id = dataset_id - self.name = name - self.output_uri = output_uri + self.allocate_strategy = allocate_strategy + self.creator_id = creator_id + self.description = description + self.gmt_created_time = gmt_created_time + self.gmt_modified_time = gmt_modified_time + self.labels = labels + self.latest_operation_id = latest_operation_id + self.min = min + self.parent_quota_id = parent_quota_id + self.queue_strategy = queue_strategy + self.quota_config = quota_config + self.quota_details = quota_details + # Quota Id + self.quota_id = quota_id + self.quota_name = quota_name + self.reason_code = reason_code + self.reason_message = reason_message + self.request_id = request_id + self.resource_group_ids = resource_group_ids + self.resource_type = resource_type + self.status = status + self.sub_quotas = sub_quotas + self.workspaces = workspaces def validate(self): - pass + if self.labels: + for k in self.labels: + if k: + k.validate() + if self.min: + self.min.validate() + if self.quota_config: + self.quota_config.validate() + if self.quota_details: + self.quota_details.validate() + if self.sub_quotas: + for k in self.sub_quotas: + if k: + k.validate() + if self.workspaces: + for k in self.workspaces: + if k: + k.validate() def to_map(self): _map = super().to_map() @@ -10150,34 +10632,133 @@ def to_map(self): return _map result = dict() - if self.dataset_id is not None: - result['DatasetId'] = self.dataset_id - if self.name is not None: - result['Name'] = self.name - if self.output_uri is not None: - result['OutputUri'] = self.output_uri + if self.allocate_strategy is not None: + result['AllocateStrategy'] = self.allocate_strategy + if self.creator_id is not None: + result['CreatorId'] = self.creator_id + if self.description is not None: + result['Description'] = self.description + if self.gmt_created_time is not None: + result['GmtCreatedTime'] = self.gmt_created_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + if self.latest_operation_id is not None: + result['LatestOperationId'] = self.latest_operation_id + if self.min is not None: + result['Min'] = self.min.to_map() + if self.parent_quota_id is not None: + result['ParentQuotaId'] = self.parent_quota_id + if self.queue_strategy is not None: + result['QueueStrategy'] = self.queue_strategy + if self.quota_config is not None: + result['QuotaConfig'] = self.quota_config.to_map() + if self.quota_details is not None: + result['QuotaDetails'] = self.quota_details.to_map() + if self.quota_id is not None: + result['QuotaId'] = self.quota_id + if self.quota_name is not None: + result['QuotaName'] = self.quota_name + if self.reason_code is not None: + result['ReasonCode'] = self.reason_code + if self.reason_message is not None: + result['ReasonMessage'] = self.reason_message + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.resource_group_ids is not None: + result['ResourceGroupIds'] = self.resource_group_ids + if self.resource_type is not None: + result['ResourceType'] = self.resource_type + if self.status is not None: + result['Status'] = self.status + result['SubQuotas'] = [] + if self.sub_quotas is not None: + for k in self.sub_quotas: + result['SubQuotas'].append(k.to_map() if k else None) + result['Workspaces'] = [] + if self.workspaces is not None: + for k in self.workspaces: + result['Workspaces'].append(k.to_map() if k else None) return result def from_map(self, m: dict = None): m = m or dict() - if m.get('DatasetId') is not None: - self.dataset_id = m.get('DatasetId') - if m.get('Name') is not None: - self.name = m.get('Name') - if m.get('OutputUri') is not None: - self.output_uri = m.get('OutputUri') + if m.get('AllocateStrategy') is not None: + self.allocate_strategy = m.get('AllocateStrategy') + if m.get('CreatorId') is not None: + self.creator_id = m.get('CreatorId') + if m.get('Description') is not None: + self.description = m.get('Description') + if m.get('GmtCreatedTime') is not None: + self.gmt_created_time = m.get('GmtCreatedTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = Label() + self.labels.append(temp_model.from_map(k)) + if m.get('LatestOperationId') is not None: + self.latest_operation_id = m.get('LatestOperationId') + if m.get('Min') is not None: + temp_model = ResourceSpec() + self.min = temp_model.from_map(m['Min']) + if m.get('ParentQuotaId') is not None: + self.parent_quota_id = m.get('ParentQuotaId') + if m.get('QueueStrategy') is not None: + self.queue_strategy = m.get('QueueStrategy') + if m.get('QuotaConfig') is not None: + temp_model = QuotaConfig() + self.quota_config = temp_model.from_map(m['QuotaConfig']) + if m.get('QuotaDetails') is not None: + temp_model = QuotaDetails() + self.quota_details = temp_model.from_map(m['QuotaDetails']) + if m.get('QuotaId') is not None: + self.quota_id = m.get('QuotaId') + if m.get('QuotaName') is not None: + self.quota_name = m.get('QuotaName') + if m.get('ReasonCode') is not None: + self.reason_code = m.get('ReasonCode') + if m.get('ReasonMessage') is not None: + self.reason_message = m.get('ReasonMessage') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('ResourceGroupIds') is not None: + self.resource_group_ids = m.get('ResourceGroupIds') + if m.get('ResourceType') is not None: + self.resource_type = m.get('ResourceType') + if m.get('Status') is not None: + self.status = m.get('Status') + self.sub_quotas = [] + if m.get('SubQuotas') is not None: + for k in m.get('SubQuotas'): + temp_model = QuotaIdName() + self.sub_quotas.append(temp_model.from_map(k)) + self.workspaces = [] + if m.get('Workspaces') is not None: + for k in m.get('Workspaces'): + temp_model = WorkspaceIdName() + self.workspaces.append(temp_model.from_map(k)) return self -class GetTrainingJobResponseBodyScheduler(TeaModel): +class GetQuotaResponse(TeaModel): def __init__( self, - max_running_time_in_seconds: int = None, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetQuotaResponseBody = None, ): - self.max_running_time_in_seconds = max_running_time_in_seconds + self.headers = headers + self.status_code = status_code + self.body = body def validate(self): - pass + if self.body: + self.body.validate() def to_map(self): _map = super().to_map() @@ -10185,31 +10766,46 @@ def to_map(self): return _map result = dict() - if self.max_running_time_in_seconds is not None: - result['MaxRunningTimeInSeconds'] = self.max_running_time_in_seconds + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() return result def from_map(self, m: dict = None): m = m or dict() - if m.get('MaxRunningTimeInSeconds') is not None: - self.max_running_time_in_seconds = m.get('MaxRunningTimeInSeconds') + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetQuotaResponseBody() + self.body = temp_model.from_map(m['body']) return self -class GetTrainingJobResponseBodyStatusTransitions(TeaModel): +class GetQuotaJobViewMetricsRequest(TeaModel): def __init__( self, end_time: str = None, - reason_code: str = None, - reason_message: str = None, + order: str = None, + page_number: int = None, + page_size: int = None, + sort_by: str = None, start_time: str = None, - status: str = None, + time_step: str = None, + workspace_id: str = None, ): self.end_time = end_time - self.reason_code = reason_code - self.reason_message = reason_message + self.order = order + self.page_number = page_number + self.page_size = page_size + self.sort_by = sort_by self.start_time = start_time - self.status = status + self.time_step = time_step + self.workspace_id = workspace_id def validate(self): pass @@ -10222,46 +10818,65 @@ def to_map(self): result = dict() if self.end_time is not None: result['EndTime'] = self.end_time - if self.reason_code is not None: - result['ReasonCode'] = self.reason_code - if self.reason_message is not None: - result['ReasonMessage'] = self.reason_message + if self.order is not None: + result['Order'] = self.order + if self.page_number is not None: + result['PageNumber'] = self.page_number + if self.page_size is not None: + result['PageSize'] = self.page_size + if self.sort_by is not None: + result['SortBy'] = self.sort_by if self.start_time is not None: result['StartTime'] = self.start_time - if self.status is not None: - result['Status'] = self.status + if self.time_step is not None: + result['TimeStep'] = self.time_step + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() if m.get('EndTime') is not None: self.end_time = m.get('EndTime') - if m.get('ReasonCode') is not None: - self.reason_code = m.get('ReasonCode') - if m.get('ReasonMessage') is not None: - self.reason_message = m.get('ReasonMessage') + if m.get('Order') is not None: + self.order = m.get('Order') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + if m.get('SortBy') is not None: + self.sort_by = m.get('SortBy') if m.get('StartTime') is not None: self.start_time = m.get('StartTime') - if m.get('Status') is not None: - self.status = m.get('Status') + if m.get('TimeStep') is not None: + self.time_step = m.get('TimeStep') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class GetTrainingJobResponseBodyUserVpc(TeaModel): +class GetQuotaJobViewMetricsResponseBody(TeaModel): def __init__( self, - extended_cidrs: List[str] = None, - security_group_id: str = None, - switch_id: str = None, - vpc_id: str = None, + job_metrics: List[QuotaJobViewMetric] = None, + quota_id: str = None, + request_id: str = None, + summary: QuotaJobViewMetric = None, + total_count: int = None, ): - self.extended_cidrs = extended_cidrs - self.security_group_id = security_group_id - self.switch_id = switch_id - self.vpc_id = vpc_id + self.job_metrics = job_metrics + self.quota_id = quota_id + self.request_id = request_id + self.summary = summary + self.total_count = total_count def validate(self): - pass + if self.job_metrics: + for k in self.job_metrics: + if k: + k.validate() + if self.summary: + self.summary.validate() def to_map(self): _map = super().to_map() @@ -10269,133 +10884,4554 @@ def to_map(self): return _map result = dict() - if self.extended_cidrs is not None: - result['ExtendedCIDRs'] = self.extended_cidrs - if self.security_group_id is not None: - result['SecurityGroupId'] = self.security_group_id - if self.switch_id is not None: - result['SwitchId'] = self.switch_id - if self.vpc_id is not None: - result['VpcId'] = self.vpc_id + result['JobMetrics'] = [] + if self.job_metrics is not None: + for k in self.job_metrics: + result['JobMetrics'].append(k.to_map() if k else None) + if self.quota_id is not None: + result['QuotaId'] = self.quota_id + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.summary is not None: + result['Summary'] = self.summary.to_map() + if self.total_count is not None: + result['TotalCount'] = self.total_count return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ExtendedCIDRs') is not None: - self.extended_cidrs = m.get('ExtendedCIDRs') - if m.get('SecurityGroupId') is not None: - self.security_group_id = m.get('SecurityGroupId') - if m.get('SwitchId') is not None: - self.switch_id = m.get('SwitchId') - if m.get('VpcId') is not None: - self.vpc_id = m.get('VpcId') + self.job_metrics = [] + if m.get('JobMetrics') is not None: + for k in m.get('JobMetrics'): + temp_model = QuotaJobViewMetric() + self.job_metrics.append(temp_model.from_map(k)) + if m.get('QuotaId') is not None: + self.quota_id = m.get('QuotaId') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('Summary') is not None: + temp_model = QuotaJobViewMetric() + self.summary = temp_model.from_map(m['Summary']) + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') return self -class GetTrainingJobResponseBody(TeaModel): +class GetQuotaJobViewMetricsResponse(TeaModel): def __init__( self, - algorithm_id: str = None, - algorithm_name: str = None, - algorithm_provider: str = None, - algorithm_spec: AlgorithmSpec = None, - algorithm_version: str = None, - compute_resource: GetTrainingJobResponseBodyComputeResource = None, - gmt_create_time: str = None, - gmt_modified_time: str = None, - hyper_parameters: List[GetTrainingJobResponseBodyHyperParameters] = None, - input_channels: List[GetTrainingJobResponseBodyInputChannels] = None, - instances: List[GetTrainingJobResponseBodyInstances] = None, - is_temp_algo: bool = None, - labels: List[GetTrainingJobResponseBodyLabels] = None, - latest_metrics: List[GetTrainingJobResponseBodyLatestMetrics] = None, - latest_progress: GetTrainingJobResponseBodyLatestProgress = None, - output_channels: List[GetTrainingJobResponseBodyOutputChannels] = None, - reason_code: str = None, - reason_message: str = None, - request_id: str = None, - role_arn: str = None, - scheduler: GetTrainingJobResponseBodyScheduler = None, - status: str = None, - status_transitions: List[GetTrainingJobResponseBodyStatusTransitions] = None, - training_job_description: str = None, - training_job_id: str = None, - training_job_name: str = None, - training_job_url: str = None, - user_id: str = None, - user_vpc: GetTrainingJobResponseBodyUserVpc = None, - workspace_id: str = None, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetQuotaJobViewMetricsResponseBody = None, ): - self.algorithm_id = algorithm_id - self.algorithm_name = algorithm_name - self.algorithm_provider = algorithm_provider - self.algorithm_spec = algorithm_spec - self.algorithm_version = algorithm_version - self.compute_resource = compute_resource - self.gmt_create_time = gmt_create_time - self.gmt_modified_time = gmt_modified_time - self.hyper_parameters = hyper_parameters + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetQuotaJobViewMetricsResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetQuotaMetricsRequest(TeaModel): + def __init__( + self, + end_time: str = None, + gputype: str = None, + start_time: str = None, + time_step: str = None, + ): + self.end_time = end_time + self.gputype = gputype + self.start_time = start_time + self.time_step = time_step + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.end_time is not None: + result['EndTime'] = self.end_time + if self.gputype is not None: + result['GPUType'] = self.gputype + if self.start_time is not None: + result['StartTime'] = self.start_time + if self.time_step is not None: + result['TimeStep'] = self.time_step + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('EndTime') is not None: + self.end_time = m.get('EndTime') + if m.get('GPUType') is not None: + self.gputype = m.get('GPUType') + if m.get('StartTime') is not None: + self.start_time = m.get('StartTime') + if m.get('TimeStep') is not None: + self.time_step = m.get('TimeStep') + return self + + +class GetQuotaMetricsResponseBody(TeaModel): + def __init__( + self, + quota_id: str = None, + quota_metrics: List[QuotaMetric] = None, + request_id: str = None, + ): + self.quota_id = quota_id + self.quota_metrics = quota_metrics + self.request_id = request_id + + def validate(self): + if self.quota_metrics: + for k in self.quota_metrics: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.quota_id is not None: + result['QuotaId'] = self.quota_id + result['QuotaMetrics'] = [] + if self.quota_metrics is not None: + for k in self.quota_metrics: + result['QuotaMetrics'].append(k.to_map() if k else None) + if self.request_id is not None: + result['RequestId'] = self.request_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('QuotaId') is not None: + self.quota_id = m.get('QuotaId') + self.quota_metrics = [] + if m.get('QuotaMetrics') is not None: + for k in m.get('QuotaMetrics'): + temp_model = QuotaMetric() + self.quota_metrics.append(temp_model.from_map(k)) + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + return self + + +class GetQuotaMetricsResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetQuotaMetricsResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetQuotaMetricsResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetQuotaNodeMetricsRequest(TeaModel): + def __init__( + self, + end_time: str = None, + gputype: str = None, + start_time: str = None, + time_step: str = None, + verbose: bool = None, + ): + self.end_time = end_time + self.gputype = gputype + self.start_time = start_time + self.time_step = time_step + self.verbose = verbose + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.end_time is not None: + result['EndTime'] = self.end_time + if self.gputype is not None: + result['GPUType'] = self.gputype + if self.start_time is not None: + result['StartTime'] = self.start_time + if self.time_step is not None: + result['TimeStep'] = self.time_step + if self.verbose is not None: + result['Verbose'] = self.verbose + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('EndTime') is not None: + self.end_time = m.get('EndTime') + if m.get('GPUType') is not None: + self.gputype = m.get('GPUType') + if m.get('StartTime') is not None: + self.start_time = m.get('StartTime') + if m.get('TimeStep') is not None: + self.time_step = m.get('TimeStep') + if m.get('Verbose') is not None: + self.verbose = m.get('Verbose') + return self + + +class GetQuotaNodeMetricsResponseBody(TeaModel): + def __init__( + self, + metric_type: str = None, + nodes_metrics: List[NodeMetric] = None, + quota_id: str = None, + request_id: str = None, + ): + self.metric_type = metric_type + self.nodes_metrics = nodes_metrics + self.quota_id = quota_id + self.request_id = request_id + + def validate(self): + if self.nodes_metrics: + for k in self.nodes_metrics: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.metric_type is not None: + result['MetricType'] = self.metric_type + result['NodesMetrics'] = [] + if self.nodes_metrics is not None: + for k in self.nodes_metrics: + result['NodesMetrics'].append(k.to_map() if k else None) + if self.quota_id is not None: + result['QuotaId'] = self.quota_id + if self.request_id is not None: + result['RequestId'] = self.request_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('MetricType') is not None: + self.metric_type = m.get('MetricType') + self.nodes_metrics = [] + if m.get('NodesMetrics') is not None: + for k in m.get('NodesMetrics'): + temp_model = NodeMetric() + self.nodes_metrics.append(temp_model.from_map(k)) + if m.get('QuotaId') is not None: + self.quota_id = m.get('QuotaId') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + return self + + +class GetQuotaNodeMetricsResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetQuotaNodeMetricsResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetQuotaNodeMetricsResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetQuotaNodeViewMetricsRequest(TeaModel): + def __init__( + self, + node_id: str = None, + node_status: str = None, + order: str = None, + order_status: str = None, + page_number: int = None, + page_size: int = None, + resource_group_id: str = None, + self_only: bool = None, + sort_by: str = None, + time_step: str = None, + workspace_id: str = None, + ): + self.node_id = node_id + self.node_status = node_status + self.order = order + self.order_status = order_status + self.page_number = page_number + self.page_size = page_size + self.resource_group_id = resource_group_id + self.self_only = self_only + self.sort_by = sort_by + self.time_step = time_step + self.workspace_id = workspace_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.node_id is not None: + result['NodeId'] = self.node_id + if self.node_status is not None: + result['NodeStatus'] = self.node_status + if self.order is not None: + result['Order'] = self.order + if self.order_status is not None: + result['OrderStatus'] = self.order_status + if self.page_number is not None: + result['PageNumber'] = self.page_number + if self.page_size is not None: + result['PageSize'] = self.page_size + if self.resource_group_id is not None: + result['ResourceGroupId'] = self.resource_group_id + if self.self_only is not None: + result['SelfOnly'] = self.self_only + if self.sort_by is not None: + result['SortBy'] = self.sort_by + if self.time_step is not None: + result['TimeStep'] = self.time_step + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('NodeId') is not None: + self.node_id = m.get('NodeId') + if m.get('NodeStatus') is not None: + self.node_status = m.get('NodeStatus') + if m.get('Order') is not None: + self.order = m.get('Order') + if m.get('OrderStatus') is not None: + self.order_status = m.get('OrderStatus') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + if m.get('ResourceGroupId') is not None: + self.resource_group_id = m.get('ResourceGroupId') + if m.get('SelfOnly') is not None: + self.self_only = m.get('SelfOnly') + if m.get('SortBy') is not None: + self.sort_by = m.get('SortBy') + if m.get('TimeStep') is not None: + self.time_step = m.get('TimeStep') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') + return self + + +class GetQuotaNodeViewMetricsResponseBody(TeaModel): + def __init__( + self, + node_metrics: List[QuotaNodeViewMetric] = None, + quota_id: str = None, + request_id: str = None, + total_count: int = None, + ): + self.node_metrics = node_metrics + self.quota_id = quota_id + self.request_id = request_id + self.total_count = total_count + + def validate(self): + if self.node_metrics: + for k in self.node_metrics: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + result['NodeMetrics'] = [] + if self.node_metrics is not None: + for k in self.node_metrics: + result['NodeMetrics'].append(k.to_map() if k else None) + if self.quota_id is not None: + result['QuotaId'] = self.quota_id + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.total_count is not None: + result['TotalCount'] = self.total_count + return result + + def from_map(self, m: dict = None): + m = m or dict() + self.node_metrics = [] + if m.get('NodeMetrics') is not None: + for k in m.get('NodeMetrics'): + temp_model = QuotaNodeViewMetric() + self.node_metrics.append(temp_model.from_map(k)) + if m.get('QuotaId') is not None: + self.quota_id = m.get('QuotaId') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') + return self + + +class GetQuotaNodeViewMetricsResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetQuotaNodeViewMetricsResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetQuotaNodeViewMetricsResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetQuotaQueueInfoRequest(TeaModel): + def __init__( + self, + before_workload_id: str = None, + order: str = None, + page_number: int = None, + page_size: int = None, + show_own: bool = None, + sort_by: str = None, + status: str = None, + sub_quota_ids: str = None, + workload_ids: str = None, + workload_type: str = None, + workspace_ids: str = None, + ): + self.before_workload_id = before_workload_id + self.order = order + self.page_number = page_number + self.page_size = page_size + self.show_own = show_own + self.sort_by = sort_by + self.status = status + self.sub_quota_ids = sub_quota_ids + self.workload_ids = workload_ids + self.workload_type = workload_type + self.workspace_ids = workspace_ids + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.before_workload_id is not None: + result['BeforeWorkloadId'] = self.before_workload_id + if self.order is not None: + result['Order'] = self.order + if self.page_number is not None: + result['PageNumber'] = self.page_number + if self.page_size is not None: + result['PageSize'] = self.page_size + if self.show_own is not None: + result['ShowOwn'] = self.show_own + if self.sort_by is not None: + result['SortBy'] = self.sort_by + if self.status is not None: + result['Status'] = self.status + if self.sub_quota_ids is not None: + result['SubQuotaIds'] = self.sub_quota_ids + if self.workload_ids is not None: + result['WorkloadIds'] = self.workload_ids + if self.workload_type is not None: + result['WorkloadType'] = self.workload_type + if self.workspace_ids is not None: + result['WorkspaceIds'] = self.workspace_ids + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('BeforeWorkloadId') is not None: + self.before_workload_id = m.get('BeforeWorkloadId') + if m.get('Order') is not None: + self.order = m.get('Order') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + if m.get('ShowOwn') is not None: + self.show_own = m.get('ShowOwn') + if m.get('SortBy') is not None: + self.sort_by = m.get('SortBy') + if m.get('Status') is not None: + self.status = m.get('Status') + if m.get('SubQuotaIds') is not None: + self.sub_quota_ids = m.get('SubQuotaIds') + if m.get('WorkloadIds') is not None: + self.workload_ids = m.get('WorkloadIds') + if m.get('WorkloadType') is not None: + self.workload_type = m.get('WorkloadType') + if m.get('WorkspaceIds') is not None: + self.workspace_ids = m.get('WorkspaceIds') + return self + + +class GetQuotaQueueInfoResponseBody(TeaModel): + def __init__( + self, + queue_infos: List[QueueInfo] = None, + request_id: str = None, + total_count: int = None, + ): + self.queue_infos = queue_infos + self.request_id = request_id + self.total_count = total_count + + def validate(self): + if self.queue_infos: + for k in self.queue_infos: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + result['QueueInfos'] = [] + if self.queue_infos is not None: + for k in self.queue_infos: + result['QueueInfos'].append(k.to_map() if k else None) + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.total_count is not None: + result['TotalCount'] = self.total_count + return result + + def from_map(self, m: dict = None): + m = m or dict() + self.queue_infos = [] + if m.get('QueueInfos') is not None: + for k in m.get('QueueInfos'): + temp_model = QueueInfo() + self.queue_infos.append(temp_model.from_map(k)) + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') + return self + + +class GetQuotaQueueInfoResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetQuotaQueueInfoResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetQuotaQueueInfoResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetQuotaRangeUserViewMetricsRequest(TeaModel): + def __init__( + self, + end_time: str = None, + order: str = None, + page_number: int = None, + page_size: int = None, + sort_by: str = None, + start_time: str = None, + user_id: str = None, + workspace_id: str = None, + ): + self.end_time = end_time + self.order = order + self.page_number = page_number + self.page_size = page_size + self.sort_by = sort_by + self.start_time = start_time + self.user_id = user_id + self.workspace_id = workspace_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.end_time is not None: + result['EndTime'] = self.end_time + if self.order is not None: + result['Order'] = self.order + if self.page_number is not None: + result['PageNumber'] = self.page_number + if self.page_size is not None: + result['PageSize'] = self.page_size + if self.sort_by is not None: + result['SortBy'] = self.sort_by + if self.start_time is not None: + result['StartTime'] = self.start_time + if self.user_id is not None: + result['UserId'] = self.user_id + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('EndTime') is not None: + self.end_time = m.get('EndTime') + if m.get('Order') is not None: + self.order = m.get('Order') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + if m.get('SortBy') is not None: + self.sort_by = m.get('SortBy') + if m.get('StartTime') is not None: + self.start_time = m.get('StartTime') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') + return self + + +class GetQuotaRangeUserViewMetricsResponseBody(TeaModel): + def __init__( + self, + quota_id: str = None, + request_id: str = None, + summary: QuotaUserViewMetric = None, + total_count: int = None, + user_metrics: List[QuotaUserViewMetric] = None, + ): + self.quota_id = quota_id + self.request_id = request_id + self.summary = summary + self.total_count = total_count + self.user_metrics = user_metrics + + def validate(self): + if self.summary: + self.summary.validate() + if self.user_metrics: + for k in self.user_metrics: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.quota_id is not None: + result['QuotaId'] = self.quota_id + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.summary is not None: + result['Summary'] = self.summary.to_map() + if self.total_count is not None: + result['TotalCount'] = self.total_count + result['UserMetrics'] = [] + if self.user_metrics is not None: + for k in self.user_metrics: + result['UserMetrics'].append(k.to_map() if k else None) + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('QuotaId') is not None: + self.quota_id = m.get('QuotaId') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('Summary') is not None: + temp_model = QuotaUserViewMetric() + self.summary = temp_model.from_map(m['Summary']) + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') + self.user_metrics = [] + if m.get('UserMetrics') is not None: + for k in m.get('UserMetrics'): + temp_model = QuotaUserViewMetric() + self.user_metrics.append(temp_model.from_map(k)) + return self + + +class GetQuotaRangeUserViewMetricsResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetQuotaRangeUserViewMetricsResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetQuotaRangeUserViewMetricsResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetQuotaUserViewMetricsRequest(TeaModel): + def __init__( + self, + order: str = None, + page_number: str = None, + page_size: str = None, + sort_by: str = None, + time_step: str = None, + user_id: str = None, + workspace_id: str = None, + ): + self.order = order + self.page_number = page_number + self.page_size = page_size + self.sort_by = sort_by + self.time_step = time_step + self.user_id = user_id + self.workspace_id = workspace_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.order is not None: + result['Order'] = self.order + if self.page_number is not None: + result['PageNumber'] = self.page_number + if self.page_size is not None: + result['PageSize'] = self.page_size + if self.sort_by is not None: + result['SortBy'] = self.sort_by + if self.time_step is not None: + result['TimeStep'] = self.time_step + if self.user_id is not None: + result['UserId'] = self.user_id + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Order') is not None: + self.order = m.get('Order') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + if m.get('SortBy') is not None: + self.sort_by = m.get('SortBy') + if m.get('TimeStep') is not None: + self.time_step = m.get('TimeStep') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') + return self + + +class GetQuotaUserViewMetricsResponseBody(TeaModel): + def __init__( + self, + quota_id: str = None, + request_id: str = None, + summary: QuotaUserViewMetric = None, + total_count: int = None, + user_metrics: List[QuotaUserViewMetric] = None, + ): + self.quota_id = quota_id + self.request_id = request_id + self.summary = summary + self.total_count = total_count + self.user_metrics = user_metrics + + def validate(self): + if self.summary: + self.summary.validate() + if self.user_metrics: + for k in self.user_metrics: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.quota_id is not None: + result['QuotaId'] = self.quota_id + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.summary is not None: + result['Summary'] = self.summary.to_map() + if self.total_count is not None: + result['TotalCount'] = self.total_count + result['UserMetrics'] = [] + if self.user_metrics is not None: + for k in self.user_metrics: + result['UserMetrics'].append(k.to_map() if k else None) + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('QuotaId') is not None: + self.quota_id = m.get('QuotaId') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('Summary') is not None: + temp_model = QuotaUserViewMetric() + self.summary = temp_model.from_map(m['Summary']) + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') + self.user_metrics = [] + if m.get('UserMetrics') is not None: + for k in m.get('UserMetrics'): + temp_model = QuotaUserViewMetric() + self.user_metrics.append(temp_model.from_map(k)) + return self + + +class GetQuotaUserViewMetricsResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetQuotaUserViewMetricsResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetQuotaUserViewMetricsResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetRangeUserViewMetricsRequest(TeaModel): + def __init__( + self, + end_time: str = None, + order: str = None, + page_number: int = None, + page_size: int = None, + sort_by: str = None, + start_time: str = None, + user_id: str = None, + workspace_id: str = None, + ): + self.end_time = end_time + self.order = order + self.page_number = page_number + self.page_size = page_size + self.sort_by = sort_by + self.start_time = start_time + self.user_id = user_id + self.workspace_id = workspace_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.end_time is not None: + result['EndTime'] = self.end_time + if self.order is not None: + result['Order'] = self.order + if self.page_number is not None: + result['PageNumber'] = self.page_number + if self.page_size is not None: + result['PageSize'] = self.page_size + if self.sort_by is not None: + result['SortBy'] = self.sort_by + if self.start_time is not None: + result['StartTime'] = self.start_time + if self.user_id is not None: + result['UserId'] = self.user_id + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('EndTime') is not None: + self.end_time = m.get('EndTime') + if m.get('Order') is not None: + self.order = m.get('Order') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + if m.get('SortBy') is not None: + self.sort_by = m.get('SortBy') + if m.get('StartTime') is not None: + self.start_time = m.get('StartTime') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') + return self + + +class GetRangeUserViewMetricsResponseBody(TeaModel): + def __init__( + self, + summary: UserViewMetric = None, + user_metrics: List[UserViewMetric] = None, + request_id: str = None, + ): + self.summary = summary + self.user_metrics = user_metrics + self.request_id = request_id + + def validate(self): + if self.summary: + self.summary.validate() + if self.user_metrics: + for k in self.user_metrics: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.summary is not None: + result['Summary'] = self.summary.to_map() + result['UserMetrics'] = [] + if self.user_metrics is not None: + for k in self.user_metrics: + result['UserMetrics'].append(k.to_map() if k else None) + if self.request_id is not None: + result['requestId'] = self.request_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Summary') is not None: + temp_model = UserViewMetric() + self.summary = temp_model.from_map(m['Summary']) + self.user_metrics = [] + if m.get('UserMetrics') is not None: + for k in m.get('UserMetrics'): + temp_model = UserViewMetric() + self.user_metrics.append(temp_model.from_map(k)) + if m.get('requestId') is not None: + self.request_id = m.get('requestId') + return self + + +class GetRangeUserViewMetricsResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetRangeUserViewMetricsResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetRangeUserViewMetricsResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetResourceGroupRequestTag(TeaModel): + def __init__( + self, + key: str = None, + value: str = None, + ): + self.key = key + self.value = value + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.key is not None: + result['Key'] = self.key + if self.value is not None: + result['Value'] = self.value + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Key') is not None: + self.key = m.get('Key') + if m.get('Value') is not None: + self.value = m.get('Value') + return self + + +class GetResourceGroupRequest(TeaModel): + def __init__( + self, + is_aiworkspace_data_enabled: bool = None, + tag: List[GetResourceGroupRequestTag] = None, + ): + self.is_aiworkspace_data_enabled = is_aiworkspace_data_enabled + self.tag = tag + + def validate(self): + if self.tag: + for k in self.tag: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.is_aiworkspace_data_enabled is not None: + result['IsAIWorkspaceDataEnabled'] = self.is_aiworkspace_data_enabled + result['Tag'] = [] + if self.tag is not None: + for k in self.tag: + result['Tag'].append(k.to_map() if k else None) + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('IsAIWorkspaceDataEnabled') is not None: + self.is_aiworkspace_data_enabled = m.get('IsAIWorkspaceDataEnabled') + self.tag = [] + if m.get('Tag') is not None: + for k in m.get('Tag'): + temp_model = GetResourceGroupRequestTag() + self.tag.append(temp_model.from_map(k)) + return self + + +class GetResourceGroupShrinkRequest(TeaModel): + def __init__( + self, + is_aiworkspace_data_enabled: bool = None, + tag_shrink: str = None, + ): + self.is_aiworkspace_data_enabled = is_aiworkspace_data_enabled + self.tag_shrink = tag_shrink + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.is_aiworkspace_data_enabled is not None: + result['IsAIWorkspaceDataEnabled'] = self.is_aiworkspace_data_enabled + if self.tag_shrink is not None: + result['Tag'] = self.tag_shrink + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('IsAIWorkspaceDataEnabled') is not None: + self.is_aiworkspace_data_enabled = m.get('IsAIWorkspaceDataEnabled') + if m.get('Tag') is not None: + self.tag_shrink = m.get('Tag') + return self + + +class GetResourceGroupResponseBodyTags(TeaModel): + def __init__( + self, + tag_key: str = None, + tag_value: str = None, + ): + self.tag_key = tag_key + self.tag_value = tag_value + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.tag_key is not None: + result['TagKey'] = self.tag_key + if self.tag_value is not None: + result['TagValue'] = self.tag_value + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('TagKey') is not None: + self.tag_key = m.get('TagKey') + if m.get('TagValue') is not None: + self.tag_value = m.get('TagValue') + return self + + +class GetResourceGroupResponseBody(TeaModel): + def __init__( + self, + cluster_id: str = None, + computing_resource_provider: str = None, + creator_id: str = None, + description: str = None, + gmt_created_time: str = None, + gmt_modified_time: str = None, + name: str = None, + request_id: str = None, + resource_type: str = None, + status: str = None, + support_rdma: bool = None, + tags: List[GetResourceGroupResponseBodyTags] = None, + user_vpc: UserVpc = None, + workspace_id: str = None, + ): + self.cluster_id = cluster_id + self.computing_resource_provider = computing_resource_provider + self.creator_id = creator_id + self.description = description + self.gmt_created_time = gmt_created_time + self.gmt_modified_time = gmt_modified_time + self.name = name + self.request_id = request_id + self.resource_type = resource_type + self.status = status + self.support_rdma = support_rdma + self.tags = tags + self.user_vpc = user_vpc + self.workspace_id = workspace_id + + def validate(self): + if self.tags: + for k in self.tags: + if k: + k.validate() + if self.user_vpc: + self.user_vpc.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.cluster_id is not None: + result['ClusterID'] = self.cluster_id + if self.computing_resource_provider is not None: + result['ComputingResourceProvider'] = self.computing_resource_provider + if self.creator_id is not None: + result['CreatorID'] = self.creator_id + if self.description is not None: + result['Description'] = self.description + if self.gmt_created_time is not None: + result['GmtCreatedTime'] = self.gmt_created_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + if self.name is not None: + result['Name'] = self.name + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.resource_type is not None: + result['ResourceType'] = self.resource_type + if self.status is not None: + result['Status'] = self.status + if self.support_rdma is not None: + result['SupportRDMA'] = self.support_rdma + result['Tags'] = [] + if self.tags is not None: + for k in self.tags: + result['Tags'].append(k.to_map() if k else None) + if self.user_vpc is not None: + result['UserVpc'] = self.user_vpc.to_map() + if self.workspace_id is not None: + result['WorkspaceID'] = self.workspace_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('ClusterID') is not None: + self.cluster_id = m.get('ClusterID') + if m.get('ComputingResourceProvider') is not None: + self.computing_resource_provider = m.get('ComputingResourceProvider') + if m.get('CreatorID') is not None: + self.creator_id = m.get('CreatorID') + if m.get('Description') is not None: + self.description = m.get('Description') + if m.get('GmtCreatedTime') is not None: + self.gmt_created_time = m.get('GmtCreatedTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('ResourceType') is not None: + self.resource_type = m.get('ResourceType') + if m.get('Status') is not None: + self.status = m.get('Status') + if m.get('SupportRDMA') is not None: + self.support_rdma = m.get('SupportRDMA') + self.tags = [] + if m.get('Tags') is not None: + for k in m.get('Tags'): + temp_model = GetResourceGroupResponseBodyTags() + self.tags.append(temp_model.from_map(k)) + if m.get('UserVpc') is not None: + temp_model = UserVpc() + self.user_vpc = temp_model.from_map(m['UserVpc']) + if m.get('WorkspaceID') is not None: + self.workspace_id = m.get('WorkspaceID') + return self + + +class GetResourceGroupResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetResourceGroupResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetResourceGroupResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetResourceGroupMachineGroupRequestTag(TeaModel): + def __init__( + self, + key: str = None, + value: str = None, + ): + self.key = key + self.value = value + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.key is not None: + result['Key'] = self.key + if self.value is not None: + result['Value'] = self.value + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Key') is not None: + self.key = m.get('Key') + if m.get('Value') is not None: + self.value = m.get('Value') + return self + + +class GetResourceGroupMachineGroupRequest(TeaModel): + def __init__( + self, + tag: List[GetResourceGroupMachineGroupRequestTag] = None, + ): + self.tag = tag + + def validate(self): + if self.tag: + for k in self.tag: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + result['Tag'] = [] + if self.tag is not None: + for k in self.tag: + result['Tag'].append(k.to_map() if k else None) + return result + + def from_map(self, m: dict = None): + m = m or dict() + self.tag = [] + if m.get('Tag') is not None: + for k in m.get('Tag'): + temp_model = GetResourceGroupMachineGroupRequestTag() + self.tag.append(temp_model.from_map(k)) + return self + + +class GetResourceGroupMachineGroupShrinkRequest(TeaModel): + def __init__( + self, + tag_shrink: str = None, + ): + self.tag_shrink = tag_shrink + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.tag_shrink is not None: + result['Tag'] = self.tag_shrink + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Tag') is not None: + self.tag_shrink = m.get('Tag') + return self + + +class GetResourceGroupMachineGroupResponseBodyTags(TeaModel): + def __init__( + self, + tag_key: str = None, + tag_value: str = None, + ): + self.tag_key = tag_key + self.tag_value = tag_value + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.tag_key is not None: + result['TagKey'] = self.tag_key + if self.tag_value is not None: + result['TagValue'] = self.tag_value + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('TagKey') is not None: + self.tag_key = m.get('TagKey') + if m.get('TagValue') is not None: + self.tag_value = m.get('TagValue') + return self + + +class GetResourceGroupMachineGroupResponseBody(TeaModel): + def __init__( + self, + cpu: str = None, + default_driver: str = None, + ecs_count: int = None, + ecs_spec: str = None, + gmt_created_time: str = None, + gmt_expired_time: str = None, + gmt_modified_time: str = None, + gmt_started_time: str = None, + gpu: str = None, + gpu_type: str = None, + machine_group_id: str = None, + memory: str = None, + name: str = None, + payment_duration: str = None, + payment_duration_unit: str = None, + payment_type: str = None, + request_id: str = None, + resource_group_id: str = None, + status: str = None, + supported_drivers: List[str] = None, + tags: List[GetResourceGroupMachineGroupResponseBodyTags] = None, + ): + self.cpu = cpu + self.default_driver = default_driver + self.ecs_count = ecs_count + self.ecs_spec = ecs_spec + self.gmt_created_time = gmt_created_time + self.gmt_expired_time = gmt_expired_time + self.gmt_modified_time = gmt_modified_time + self.gmt_started_time = gmt_started_time + self.gpu = gpu + self.gpu_type = gpu_type + self.machine_group_id = machine_group_id + self.memory = memory + self.name = name + self.payment_duration = payment_duration + self.payment_duration_unit = payment_duration_unit + self.payment_type = payment_type + self.request_id = request_id + self.resource_group_id = resource_group_id + self.status = status + self.supported_drivers = supported_drivers + self.tags = tags + + def validate(self): + if self.tags: + for k in self.tags: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.cpu is not None: + result['Cpu'] = self.cpu + if self.default_driver is not None: + result['DefaultDriver'] = self.default_driver + if self.ecs_count is not None: + result['EcsCount'] = self.ecs_count + if self.ecs_spec is not None: + result['EcsSpec'] = self.ecs_spec + if self.gmt_created_time is not None: + result['GmtCreatedTime'] = self.gmt_created_time + if self.gmt_expired_time is not None: + result['GmtExpiredTime'] = self.gmt_expired_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + if self.gmt_started_time is not None: + result['GmtStartedTime'] = self.gmt_started_time + if self.gpu is not None: + result['Gpu'] = self.gpu + if self.gpu_type is not None: + result['GpuType'] = self.gpu_type + if self.machine_group_id is not None: + result['MachineGroupID'] = self.machine_group_id + if self.memory is not None: + result['Memory'] = self.memory + if self.name is not None: + result['Name'] = self.name + if self.payment_duration is not None: + result['PaymentDuration'] = self.payment_duration + if self.payment_duration_unit is not None: + result['PaymentDurationUnit'] = self.payment_duration_unit + if self.payment_type is not None: + result['PaymentType'] = self.payment_type + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.resource_group_id is not None: + result['ResourceGroupID'] = self.resource_group_id + if self.status is not None: + result['Status'] = self.status + if self.supported_drivers is not None: + result['SupportedDrivers'] = self.supported_drivers + result['Tags'] = [] + if self.tags is not None: + for k in self.tags: + result['Tags'].append(k.to_map() if k else None) + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Cpu') is not None: + self.cpu = m.get('Cpu') + if m.get('DefaultDriver') is not None: + self.default_driver = m.get('DefaultDriver') + if m.get('EcsCount') is not None: + self.ecs_count = m.get('EcsCount') + if m.get('EcsSpec') is not None: + self.ecs_spec = m.get('EcsSpec') + if m.get('GmtCreatedTime') is not None: + self.gmt_created_time = m.get('GmtCreatedTime') + if m.get('GmtExpiredTime') is not None: + self.gmt_expired_time = m.get('GmtExpiredTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + if m.get('GmtStartedTime') is not None: + self.gmt_started_time = m.get('GmtStartedTime') + if m.get('Gpu') is not None: + self.gpu = m.get('Gpu') + if m.get('GpuType') is not None: + self.gpu_type = m.get('GpuType') + if m.get('MachineGroupID') is not None: + self.machine_group_id = m.get('MachineGroupID') + if m.get('Memory') is not None: + self.memory = m.get('Memory') + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('PaymentDuration') is not None: + self.payment_duration = m.get('PaymentDuration') + if m.get('PaymentDurationUnit') is not None: + self.payment_duration_unit = m.get('PaymentDurationUnit') + if m.get('PaymentType') is not None: + self.payment_type = m.get('PaymentType') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('ResourceGroupID') is not None: + self.resource_group_id = m.get('ResourceGroupID') + if m.get('Status') is not None: + self.status = m.get('Status') + if m.get('SupportedDrivers') is not None: + self.supported_drivers = m.get('SupportedDrivers') + self.tags = [] + if m.get('Tags') is not None: + for k in m.get('Tags'): + temp_model = GetResourceGroupMachineGroupResponseBodyTags() + self.tags.append(temp_model.from_map(k)) + return self + + +class GetResourceGroupMachineGroupResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetResourceGroupMachineGroupResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetResourceGroupMachineGroupResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetResourceGroupMetricsRequest(TeaModel): + def __init__( + self, + end_time: str = None, + gputype: str = None, + start_time: str = None, + time_step: str = None, + ): + self.end_time = end_time + self.gputype = gputype + self.start_time = start_time + self.time_step = time_step + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.end_time is not None: + result['EndTime'] = self.end_time + if self.gputype is not None: + result['GPUType'] = self.gputype + if self.start_time is not None: + result['StartTime'] = self.start_time + if self.time_step is not None: + result['TimeStep'] = self.time_step + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('EndTime') is not None: + self.end_time = m.get('EndTime') + if m.get('GPUType') is not None: + self.gputype = m.get('GPUType') + if m.get('StartTime') is not None: + self.start_time = m.get('StartTime') + if m.get('TimeStep') is not None: + self.time_step = m.get('TimeStep') + return self + + +class GetResourceGroupMetricsResponseBody(TeaModel): + def __init__( + self, + request_id: str = None, + resource_group_id: str = None, + resource_group_metrics: List[ResourceGroupMetric] = None, + ): + self.request_id = request_id + self.resource_group_id = resource_group_id + self.resource_group_metrics = resource_group_metrics + + def validate(self): + if self.resource_group_metrics: + for k in self.resource_group_metrics: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.resource_group_id is not None: + result['ResourceGroupID'] = self.resource_group_id + result['ResourceGroupMetrics'] = [] + if self.resource_group_metrics is not None: + for k in self.resource_group_metrics: + result['ResourceGroupMetrics'].append(k.to_map() if k else None) + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('ResourceGroupID') is not None: + self.resource_group_id = m.get('ResourceGroupID') + self.resource_group_metrics = [] + if m.get('ResourceGroupMetrics') is not None: + for k in m.get('ResourceGroupMetrics'): + temp_model = ResourceGroupMetric() + self.resource_group_metrics.append(temp_model.from_map(k)) + return self + + +class GetResourceGroupMetricsResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetResourceGroupMetricsResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetResourceGroupMetricsResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetResourceGroupRequestRequest(TeaModel): + def __init__( + self, + pod_status: str = None, + resource_group_id: str = None, + ): + self.pod_status = pod_status + self.resource_group_id = resource_group_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.pod_status is not None: + result['PodStatus'] = self.pod_status + if self.resource_group_id is not None: + result['ResourceGroupID'] = self.resource_group_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('PodStatus') is not None: + self.pod_status = m.get('PodStatus') + if m.get('ResourceGroupID') is not None: + self.resource_group_id = m.get('ResourceGroupID') + return self + + +class GetResourceGroupRequestResponseBody(TeaModel): + def __init__( + self, + request_cpu: int = None, + request_gpu: int = None, + request_gpuinfos: List[GPUInfo] = None, + request_memory: int = None, + ): + self.request_cpu = request_cpu + self.request_gpu = request_gpu + self.request_gpuinfos = request_gpuinfos + self.request_memory = request_memory + + def validate(self): + if self.request_gpuinfos: + for k in self.request_gpuinfos: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.request_cpu is not None: + result['requestCPU'] = self.request_cpu + if self.request_gpu is not None: + result['requestGPU'] = self.request_gpu + result['requestGPUInfos'] = [] + if self.request_gpuinfos is not None: + for k in self.request_gpuinfos: + result['requestGPUInfos'].append(k.to_map() if k else None) + if self.request_memory is not None: + result['requestMemory'] = self.request_memory + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('requestCPU') is not None: + self.request_cpu = m.get('requestCPU') + if m.get('requestGPU') is not None: + self.request_gpu = m.get('requestGPU') + self.request_gpuinfos = [] + if m.get('requestGPUInfos') is not None: + for k in m.get('requestGPUInfos'): + temp_model = GPUInfo() + self.request_gpuinfos.append(temp_model.from_map(k)) + if m.get('requestMemory') is not None: + self.request_memory = m.get('requestMemory') + return self + + +class GetResourceGroupRequestResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetResourceGroupRequestResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetResourceGroupRequestResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetResourceGroupTotalRequest(TeaModel): + def __init__( + self, + resource_group_id: str = None, + ): + self.resource_group_id = resource_group_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.resource_group_id is not None: + result['ResourceGroupID'] = self.resource_group_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('ResourceGroupID') is not None: + self.resource_group_id = m.get('ResourceGroupID') + return self + + +class GetResourceGroupTotalResponseBody(TeaModel): + def __init__( + self, + total_cpu: int = None, + total_gpu: int = None, + total_gpuinfos: List[GPUInfo] = None, + total_memory: int = None, + ): + self.total_cpu = total_cpu + self.total_gpu = total_gpu + self.total_gpuinfos = total_gpuinfos + self.total_memory = total_memory + + def validate(self): + if self.total_gpuinfos: + for k in self.total_gpuinfos: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.total_cpu is not None: + result['totalCPU'] = self.total_cpu + if self.total_gpu is not None: + result['totalGPU'] = self.total_gpu + result['totalGPUInfos'] = [] + if self.total_gpuinfos is not None: + for k in self.total_gpuinfos: + result['totalGPUInfos'].append(k.to_map() if k else None) + if self.total_memory is not None: + result['totalMemory'] = self.total_memory + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('totalCPU') is not None: + self.total_cpu = m.get('totalCPU') + if m.get('totalGPU') is not None: + self.total_gpu = m.get('totalGPU') + self.total_gpuinfos = [] + if m.get('totalGPUInfos') is not None: + for k in m.get('totalGPUInfos'): + temp_model = GPUInfo() + self.total_gpuinfos.append(temp_model.from_map(k)) + if m.get('totalMemory') is not None: + self.total_memory = m.get('totalMemory') + return self + + +class GetResourceGroupTotalResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetResourceGroupTotalResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetResourceGroupTotalResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetServiceIdentityRoleResponseBody(TeaModel): + def __init__( + self, + exist: bool = None, + request_id: str = None, + role_name: str = None, + ): + self.exist = exist + self.request_id = request_id + self.role_name = role_name + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.exist is not None: + result['Exist'] = self.exist + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.role_name is not None: + result['RoleName'] = self.role_name + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Exist') is not None: + self.exist = m.get('Exist') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('RoleName') is not None: + self.role_name = m.get('RoleName') + return self + + +class GetServiceIdentityRoleResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetServiceIdentityRoleResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetServiceIdentityRoleResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetSpotPriceHistoryRequest(TeaModel): + def __init__( + self, + end_time: str = None, + order: str = None, + page_number: int = None, + page_size: int = None, + sort_by: str = None, + start_time: str = None, + ): + self.end_time = end_time + self.order = order + self.page_number = page_number + self.page_size = page_size + self.sort_by = sort_by + self.start_time = start_time + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.end_time is not None: + result['EndTime'] = self.end_time + if self.order is not None: + result['Order'] = self.order + if self.page_number is not None: + result['PageNumber'] = self.page_number + if self.page_size is not None: + result['PageSize'] = self.page_size + if self.sort_by is not None: + result['SortBy'] = self.sort_by + if self.start_time is not None: + result['StartTime'] = self.start_time + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('EndTime') is not None: + self.end_time = m.get('EndTime') + if m.get('Order') is not None: + self.order = m.get('Order') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + if m.get('SortBy') is not None: + self.sort_by = m.get('SortBy') + if m.get('StartTime') is not None: + self.start_time = m.get('StartTime') + return self + + +class GetSpotPriceHistoryResponseBody(TeaModel): + def __init__( + self, + request_id: str = None, + spot_price_history: List[SpotPriceItem] = None, + total_count: int = None, + ): + self.request_id = request_id + self.spot_price_history = spot_price_history + self.total_count = total_count + + def validate(self): + if self.spot_price_history: + for k in self.spot_price_history: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.request_id is not None: + result['RequestId'] = self.request_id + result['SpotPriceHistory'] = [] + if self.spot_price_history is not None: + for k in self.spot_price_history: + result['SpotPriceHistory'].append(k.to_map() if k else None) + if self.total_count is not None: + result['TotalCount'] = self.total_count + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + self.spot_price_history = [] + if m.get('SpotPriceHistory') is not None: + for k in m.get('SpotPriceHistory'): + temp_model = SpotPriceItem() + self.spot_price_history.append(temp_model.from_map(k)) + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') + return self + + +class GetSpotPriceHistoryResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetSpotPriceHistoryResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetSpotPriceHistoryResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetSpotStockPreviewResponseBody(TeaModel): + def __init__( + self, + instance_type: str = None, + request_id: str = None, + stock_status: str = None, + ): + self.instance_type = instance_type + self.request_id = request_id + self.stock_status = stock_status + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.instance_type is not None: + result['InstanceType'] = self.instance_type + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.stock_status is not None: + result['StockStatus'] = self.stock_status + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('InstanceType') is not None: + self.instance_type = m.get('InstanceType') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('StockStatus') is not None: + self.stock_status = m.get('StockStatus') + return self + + +class GetSpotStockPreviewResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetSpotStockPreviewResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetSpotStockPreviewResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetTokenRequest(TeaModel): + def __init__( + self, + expire_time: int = None, + training_job_id: str = None, + ): + self.expire_time = expire_time + self.training_job_id = training_job_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.expire_time is not None: + result['ExpireTime'] = self.expire_time + if self.training_job_id is not None: + result['TrainingJobId'] = self.training_job_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('ExpireTime') is not None: + self.expire_time = m.get('ExpireTime') + if m.get('TrainingJobId') is not None: + self.training_job_id = m.get('TrainingJobId') + return self + + +class GetTokenResponseBody(TeaModel): + def __init__( + self, + request_id: str = None, + token: str = None, + ): + self.request_id = request_id + self.token = token + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.token is not None: + result['Token'] = self.token + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('Token') is not None: + self.token = m.get('Token') + return self + + +class GetTokenResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetTokenResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetTokenResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetTrainingJobRequest(TeaModel): + def __init__( + self, + token: str = None, + ): + self.token = token + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.token is not None: + result['Token'] = self.token + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Token') is not None: + self.token = m.get('Token') + return self + + +class GetTrainingJobResponseBodyComputeResourceInstanceSpec(TeaModel): + def __init__( + self, + cpu: str = None, + gpu: str = None, + gputype: str = None, + memory: str = None, + shared_memory: str = None, + ): + self.cpu = cpu + self.gpu = gpu + self.gputype = gputype + self.memory = memory + self.shared_memory = shared_memory + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.cpu is not None: + result['CPU'] = self.cpu + if self.gpu is not None: + result['GPU'] = self.gpu + if self.gputype is not None: + result['GPUType'] = self.gputype + if self.memory is not None: + result['Memory'] = self.memory + if self.shared_memory is not None: + result['SharedMemory'] = self.shared_memory + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('CPU') is not None: + self.cpu = m.get('CPU') + if m.get('GPU') is not None: + self.gpu = m.get('GPU') + if m.get('GPUType') is not None: + self.gputype = m.get('GPUType') + if m.get('Memory') is not None: + self.memory = m.get('Memory') + if m.get('SharedMemory') is not None: + self.shared_memory = m.get('SharedMemory') + return self + + +class GetTrainingJobResponseBodyComputeResource(TeaModel): + def __init__( + self, + ecs_count: int = None, + ecs_spec: str = None, + instance_count: int = None, + instance_spec: GetTrainingJobResponseBodyComputeResourceInstanceSpec = None, + resource_id: str = None, + ): + self.ecs_count = ecs_count + self.ecs_spec = ecs_spec + self.instance_count = instance_count + self.instance_spec = instance_spec + self.resource_id = resource_id + + def validate(self): + if self.instance_spec: + self.instance_spec.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.ecs_count is not None: + result['EcsCount'] = self.ecs_count + if self.ecs_spec is not None: + result['EcsSpec'] = self.ecs_spec + if self.instance_count is not None: + result['InstanceCount'] = self.instance_count + if self.instance_spec is not None: + result['InstanceSpec'] = self.instance_spec.to_map() + if self.resource_id is not None: + result['ResourceId'] = self.resource_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('EcsCount') is not None: + self.ecs_count = m.get('EcsCount') + if m.get('EcsSpec') is not None: + self.ecs_spec = m.get('EcsSpec') + if m.get('InstanceCount') is not None: + self.instance_count = m.get('InstanceCount') + if m.get('InstanceSpec') is not None: + temp_model = GetTrainingJobResponseBodyComputeResourceInstanceSpec() + self.instance_spec = temp_model.from_map(m['InstanceSpec']) + if m.get('ResourceId') is not None: + self.resource_id = m.get('ResourceId') + return self + + +class GetTrainingJobResponseBodyExperimentConfig(TeaModel): + def __init__( + self, + experiment_id: str = None, + experiment_name: str = None, + ): + self.experiment_id = experiment_id + self.experiment_name = experiment_name + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.experiment_id is not None: + result['ExperimentId'] = self.experiment_id + if self.experiment_name is not None: + result['ExperimentName'] = self.experiment_name + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('ExperimentId') is not None: + self.experiment_id = m.get('ExperimentId') + if m.get('ExperimentName') is not None: + self.experiment_name = m.get('ExperimentName') + return self + + +class GetTrainingJobResponseBodyHyperParameters(TeaModel): + def __init__( + self, + name: str = None, + value: str = None, + ): + self.name = name + self.value = value + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.name is not None: + result['Name'] = self.name + if self.value is not None: + result['Value'] = self.value + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('Value') is not None: + self.value = m.get('Value') + return self + + +class GetTrainingJobResponseBodyInputChannels(TeaModel): + def __init__( + self, + dataset_id: str = None, + input_uri: str = None, + name: str = None, + ): + self.dataset_id = dataset_id + self.input_uri = input_uri + self.name = name + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.dataset_id is not None: + result['DatasetId'] = self.dataset_id + if self.input_uri is not None: + result['InputUri'] = self.input_uri + if self.name is not None: + result['Name'] = self.name + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('DatasetId') is not None: + self.dataset_id = m.get('DatasetId') + if m.get('InputUri') is not None: + self.input_uri = m.get('InputUri') + if m.get('Name') is not None: + self.name = m.get('Name') + return self + + +class GetTrainingJobResponseBodyInstances(TeaModel): + def __init__( + self, + name: str = None, + role: str = None, + status: str = None, + ): + self.name = name + self.role = role + self.status = status + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.name is not None: + result['Name'] = self.name + if self.role is not None: + result['Role'] = self.role + if self.status is not None: + result['Status'] = self.status + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('Role') is not None: + self.role = m.get('Role') + if m.get('Status') is not None: + self.status = m.get('Status') + return self + + +class GetTrainingJobResponseBodyLabels(TeaModel): + def __init__( + self, + key: str = None, + value: str = None, + ): + self.key = key + self.value = value + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.key is not None: + result['Key'] = self.key + if self.value is not None: + result['Value'] = self.value + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Key') is not None: + self.key = m.get('Key') + if m.get('Value') is not None: + self.value = m.get('Value') + return self + + +class GetTrainingJobResponseBodyLatestMetrics(TeaModel): + def __init__( + self, + name: str = None, + timestamp: str = None, + value: float = None, + ): + self.name = name + self.timestamp = timestamp + self.value = value + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.name is not None: + result['Name'] = self.name + if self.timestamp is not None: + result['Timestamp'] = self.timestamp + if self.value is not None: + result['Value'] = self.value + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('Timestamp') is not None: + self.timestamp = m.get('Timestamp') + if m.get('Value') is not None: + self.value = m.get('Value') + return self + + +class GetTrainingJobResponseBodyLatestProgressOverallProgress(TeaModel): + def __init__( + self, + timestamp: str = None, + value: float = None, + ): + self.timestamp = timestamp + self.value = value + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.timestamp is not None: + result['Timestamp'] = self.timestamp + if self.value is not None: + result['Value'] = self.value + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Timestamp') is not None: + self.timestamp = m.get('Timestamp') + if m.get('Value') is not None: + self.value = m.get('Value') + return self + + +class GetTrainingJobResponseBodyLatestProgressRemainingTime(TeaModel): + def __init__( + self, + timestamp: str = None, + value: int = None, + ): + self.timestamp = timestamp + self.value = value + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.timestamp is not None: + result['Timestamp'] = self.timestamp + if self.value is not None: + result['Value'] = self.value + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Timestamp') is not None: + self.timestamp = m.get('Timestamp') + if m.get('Value') is not None: + self.value = m.get('Value') + return self + + +class GetTrainingJobResponseBodyLatestProgress(TeaModel): + def __init__( + self, + overall_progress: GetTrainingJobResponseBodyLatestProgressOverallProgress = None, + remaining_time: GetTrainingJobResponseBodyLatestProgressRemainingTime = None, + ): + self.overall_progress = overall_progress + self.remaining_time = remaining_time + + def validate(self): + if self.overall_progress: + self.overall_progress.validate() + if self.remaining_time: + self.remaining_time.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.overall_progress is not None: + result['OverallProgress'] = self.overall_progress.to_map() + if self.remaining_time is not None: + result['RemainingTime'] = self.remaining_time.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('OverallProgress') is not None: + temp_model = GetTrainingJobResponseBodyLatestProgressOverallProgress() + self.overall_progress = temp_model.from_map(m['OverallProgress']) + if m.get('RemainingTime') is not None: + temp_model = GetTrainingJobResponseBodyLatestProgressRemainingTime() + self.remaining_time = temp_model.from_map(m['RemainingTime']) + return self + + +class GetTrainingJobResponseBodyOutputChannels(TeaModel): + def __init__( + self, + dataset_id: str = None, + name: str = None, + output_uri: str = None, + ): + self.dataset_id = dataset_id + self.name = name + self.output_uri = output_uri + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.dataset_id is not None: + result['DatasetId'] = self.dataset_id + if self.name is not None: + result['Name'] = self.name + if self.output_uri is not None: + result['OutputUri'] = self.output_uri + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('DatasetId') is not None: + self.dataset_id = m.get('DatasetId') + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('OutputUri') is not None: + self.output_uri = m.get('OutputUri') + return self + + +class GetTrainingJobResponseBodyOutputModel(TeaModel): + def __init__( + self, + output_channel_name: str = None, + uri: str = None, + ): + self.output_channel_name = output_channel_name + self.uri = uri + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.output_channel_name is not None: + result['OutputChannelName'] = self.output_channel_name + if self.uri is not None: + result['Uri'] = self.uri + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('OutputChannelName') is not None: + self.output_channel_name = m.get('OutputChannelName') + if m.get('Uri') is not None: + self.uri = m.get('Uri') + return self + + +class GetTrainingJobResponseBodyScheduler(TeaModel): + def __init__( + self, + max_running_time_in_seconds: int = None, + ): + self.max_running_time_in_seconds = max_running_time_in_seconds + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.max_running_time_in_seconds is not None: + result['MaxRunningTimeInSeconds'] = self.max_running_time_in_seconds + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('MaxRunningTimeInSeconds') is not None: + self.max_running_time_in_seconds = m.get('MaxRunningTimeInSeconds') + return self + + +class GetTrainingJobResponseBodySettings(TeaModel): + def __init__( + self, + aimaster_type: str = None, + enable_error_monitoring_in_aimaster: bool = None, + error_monitoring_args: str = None, + priority: int = None, + ): + self.aimaster_type = aimaster_type + self.enable_error_monitoring_in_aimaster = enable_error_monitoring_in_aimaster + self.error_monitoring_args = error_monitoring_args + self.priority = priority + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.aimaster_type is not None: + result['AIMasterType'] = self.aimaster_type + if self.enable_error_monitoring_in_aimaster is not None: + result['EnableErrorMonitoringInAIMaster'] = self.enable_error_monitoring_in_aimaster + if self.error_monitoring_args is not None: + result['ErrorMonitoringArgs'] = self.error_monitoring_args + if self.priority is not None: + result['Priority'] = self.priority + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('AIMasterType') is not None: + self.aimaster_type = m.get('AIMasterType') + if m.get('EnableErrorMonitoringInAIMaster') is not None: + self.enable_error_monitoring_in_aimaster = m.get('EnableErrorMonitoringInAIMaster') + if m.get('ErrorMonitoringArgs') is not None: + self.error_monitoring_args = m.get('ErrorMonitoringArgs') + if m.get('Priority') is not None: + self.priority = m.get('Priority') + return self + + +class GetTrainingJobResponseBodyStatusTransitions(TeaModel): + def __init__( + self, + end_time: str = None, + reason_code: str = None, + reason_message: str = None, + start_time: str = None, + status: str = None, + ): + self.end_time = end_time + self.reason_code = reason_code + self.reason_message = reason_message + self.start_time = start_time + self.status = status + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.end_time is not None: + result['EndTime'] = self.end_time + if self.reason_code is not None: + result['ReasonCode'] = self.reason_code + if self.reason_message is not None: + result['ReasonMessage'] = self.reason_message + if self.start_time is not None: + result['StartTime'] = self.start_time + if self.status is not None: + result['Status'] = self.status + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('EndTime') is not None: + self.end_time = m.get('EndTime') + if m.get('ReasonCode') is not None: + self.reason_code = m.get('ReasonCode') + if m.get('ReasonMessage') is not None: + self.reason_message = m.get('ReasonMessage') + if m.get('StartTime') is not None: + self.start_time = m.get('StartTime') + if m.get('Status') is not None: + self.status = m.get('Status') + return self + + +class GetTrainingJobResponseBodyUserVpc(TeaModel): + def __init__( + self, + extended_cidrs: List[str] = None, + security_group_id: str = None, + switch_id: str = None, + vpc_id: str = None, + ): + self.extended_cidrs = extended_cidrs + self.security_group_id = security_group_id + self.switch_id = switch_id + self.vpc_id = vpc_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.extended_cidrs is not None: + result['ExtendedCIDRs'] = self.extended_cidrs + if self.security_group_id is not None: + result['SecurityGroupId'] = self.security_group_id + if self.switch_id is not None: + result['SwitchId'] = self.switch_id + if self.vpc_id is not None: + result['VpcId'] = self.vpc_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('ExtendedCIDRs') is not None: + self.extended_cidrs = m.get('ExtendedCIDRs') + if m.get('SecurityGroupId') is not None: + self.security_group_id = m.get('SecurityGroupId') + if m.get('SwitchId') is not None: + self.switch_id = m.get('SwitchId') + if m.get('VpcId') is not None: + self.vpc_id = m.get('VpcId') + return self + + +class GetTrainingJobResponseBody(TeaModel): + def __init__( + self, + algorithm_id: str = None, + algorithm_name: str = None, + algorithm_provider: str = None, + algorithm_spec: AlgorithmSpec = None, + algorithm_version: str = None, + compute_resource: GetTrainingJobResponseBodyComputeResource = None, + duration: int = None, + environments: Dict[str, str] = None, + experiment_config: GetTrainingJobResponseBodyExperimentConfig = None, + gmt_create_time: str = None, + gmt_modified_time: str = None, + hyper_parameters: List[GetTrainingJobResponseBodyHyperParameters] = None, + input_channels: List[GetTrainingJobResponseBodyInputChannels] = None, + instances: List[GetTrainingJobResponseBodyInstances] = None, + is_temp_algo: bool = None, + labels: List[GetTrainingJobResponseBodyLabels] = None, + latest_metrics: List[GetTrainingJobResponseBodyLatestMetrics] = None, + latest_progress: GetTrainingJobResponseBodyLatestProgress = None, + output_channels: List[GetTrainingJobResponseBodyOutputChannels] = None, + output_model: GetTrainingJobResponseBodyOutputModel = None, + python_requirements: List[str] = None, + reason_code: str = None, + reason_message: str = None, + request_id: str = None, + role_arn: str = None, + scheduler: GetTrainingJobResponseBodyScheduler = None, + settings: GetTrainingJobResponseBodySettings = None, + status: str = None, + status_transitions: List[GetTrainingJobResponseBodyStatusTransitions] = None, + training_job_description: str = None, + training_job_id: str = None, + training_job_name: str = None, + training_job_url: str = None, + user_id: str = None, + user_vpc: GetTrainingJobResponseBodyUserVpc = None, + workspace_id: str = None, + ): + self.algorithm_id = algorithm_id + self.algorithm_name = algorithm_name + self.algorithm_provider = algorithm_provider + self.algorithm_spec = algorithm_spec + self.algorithm_version = algorithm_version + self.compute_resource = compute_resource + self.duration = duration + self.environments = environments + self.experiment_config = experiment_config + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time + self.hyper_parameters = hyper_parameters self.input_channels = input_channels self.instances = instances self.is_temp_algo = is_temp_algo self.labels = labels - self.latest_metrics = latest_metrics - self.latest_progress = latest_progress - self.output_channels = output_channels - self.reason_code = reason_code - self.reason_message = reason_message + self.latest_metrics = latest_metrics + self.latest_progress = latest_progress + self.output_channels = output_channels + self.output_model = output_model + self.python_requirements = python_requirements + self.reason_code = reason_code + self.reason_message = reason_message + self.request_id = request_id + self.role_arn = role_arn + self.scheduler = scheduler + self.settings = settings + self.status = status + self.status_transitions = status_transitions + self.training_job_description = training_job_description + self.training_job_id = training_job_id + self.training_job_name = training_job_name + self.training_job_url = training_job_url + self.user_id = user_id + self.user_vpc = user_vpc + self.workspace_id = workspace_id + + def validate(self): + if self.algorithm_spec: + self.algorithm_spec.validate() + if self.compute_resource: + self.compute_resource.validate() + if self.experiment_config: + self.experiment_config.validate() + if self.hyper_parameters: + for k in self.hyper_parameters: + if k: + k.validate() + if self.input_channels: + for k in self.input_channels: + if k: + k.validate() + if self.instances: + for k in self.instances: + if k: + k.validate() + if self.labels: + for k in self.labels: + if k: + k.validate() + if self.latest_metrics: + for k in self.latest_metrics: + if k: + k.validate() + if self.latest_progress: + self.latest_progress.validate() + if self.output_channels: + for k in self.output_channels: + if k: + k.validate() + if self.output_model: + self.output_model.validate() + if self.scheduler: + self.scheduler.validate() + if self.settings: + self.settings.validate() + if self.status_transitions: + for k in self.status_transitions: + if k: + k.validate() + if self.user_vpc: + self.user_vpc.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.algorithm_id is not None: + result['AlgorithmId'] = self.algorithm_id + if self.algorithm_name is not None: + result['AlgorithmName'] = self.algorithm_name + if self.algorithm_provider is not None: + result['AlgorithmProvider'] = self.algorithm_provider + if self.algorithm_spec is not None: + result['AlgorithmSpec'] = self.algorithm_spec.to_map() + if self.algorithm_version is not None: + result['AlgorithmVersion'] = self.algorithm_version + if self.compute_resource is not None: + result['ComputeResource'] = self.compute_resource.to_map() + if self.duration is not None: + result['Duration'] = self.duration + if self.environments is not None: + result['Environments'] = self.environments + if self.experiment_config is not None: + result['ExperimentConfig'] = self.experiment_config.to_map() + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + result['HyperParameters'] = [] + if self.hyper_parameters is not None: + for k in self.hyper_parameters: + result['HyperParameters'].append(k.to_map() if k else None) + result['InputChannels'] = [] + if self.input_channels is not None: + for k in self.input_channels: + result['InputChannels'].append(k.to_map() if k else None) + result['Instances'] = [] + if self.instances is not None: + for k in self.instances: + result['Instances'].append(k.to_map() if k else None) + if self.is_temp_algo is not None: + result['IsTempAlgo'] = self.is_temp_algo + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + result['LatestMetrics'] = [] + if self.latest_metrics is not None: + for k in self.latest_metrics: + result['LatestMetrics'].append(k.to_map() if k else None) + if self.latest_progress is not None: + result['LatestProgress'] = self.latest_progress.to_map() + result['OutputChannels'] = [] + if self.output_channels is not None: + for k in self.output_channels: + result['OutputChannels'].append(k.to_map() if k else None) + if self.output_model is not None: + result['OutputModel'] = self.output_model.to_map() + if self.python_requirements is not None: + result['PythonRequirements'] = self.python_requirements + if self.reason_code is not None: + result['ReasonCode'] = self.reason_code + if self.reason_message is not None: + result['ReasonMessage'] = self.reason_message + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.role_arn is not None: + result['RoleArn'] = self.role_arn + if self.scheduler is not None: + result['Scheduler'] = self.scheduler.to_map() + if self.settings is not None: + result['Settings'] = self.settings.to_map() + if self.status is not None: + result['Status'] = self.status + result['StatusTransitions'] = [] + if self.status_transitions is not None: + for k in self.status_transitions: + result['StatusTransitions'].append(k.to_map() if k else None) + if self.training_job_description is not None: + result['TrainingJobDescription'] = self.training_job_description + if self.training_job_id is not None: + result['TrainingJobId'] = self.training_job_id + if self.training_job_name is not None: + result['TrainingJobName'] = self.training_job_name + if self.training_job_url is not None: + result['TrainingJobUrl'] = self.training_job_url + if self.user_id is not None: + result['UserId'] = self.user_id + if self.user_vpc is not None: + result['UserVpc'] = self.user_vpc.to_map() + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('AlgorithmId') is not None: + self.algorithm_id = m.get('AlgorithmId') + if m.get('AlgorithmName') is not None: + self.algorithm_name = m.get('AlgorithmName') + if m.get('AlgorithmProvider') is not None: + self.algorithm_provider = m.get('AlgorithmProvider') + if m.get('AlgorithmSpec') is not None: + temp_model = AlgorithmSpec() + self.algorithm_spec = temp_model.from_map(m['AlgorithmSpec']) + if m.get('AlgorithmVersion') is not None: + self.algorithm_version = m.get('AlgorithmVersion') + if m.get('ComputeResource') is not None: + temp_model = GetTrainingJobResponseBodyComputeResource() + self.compute_resource = temp_model.from_map(m['ComputeResource']) + if m.get('Duration') is not None: + self.duration = m.get('Duration') + if m.get('Environments') is not None: + self.environments = m.get('Environments') + if m.get('ExperimentConfig') is not None: + temp_model = GetTrainingJobResponseBodyExperimentConfig() + self.experiment_config = temp_model.from_map(m['ExperimentConfig']) + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + self.hyper_parameters = [] + if m.get('HyperParameters') is not None: + for k in m.get('HyperParameters'): + temp_model = GetTrainingJobResponseBodyHyperParameters() + self.hyper_parameters.append(temp_model.from_map(k)) + self.input_channels = [] + if m.get('InputChannels') is not None: + for k in m.get('InputChannels'): + temp_model = GetTrainingJobResponseBodyInputChannels() + self.input_channels.append(temp_model.from_map(k)) + self.instances = [] + if m.get('Instances') is not None: + for k in m.get('Instances'): + temp_model = GetTrainingJobResponseBodyInstances() + self.instances.append(temp_model.from_map(k)) + if m.get('IsTempAlgo') is not None: + self.is_temp_algo = m.get('IsTempAlgo') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = GetTrainingJobResponseBodyLabels() + self.labels.append(temp_model.from_map(k)) + self.latest_metrics = [] + if m.get('LatestMetrics') is not None: + for k in m.get('LatestMetrics'): + temp_model = GetTrainingJobResponseBodyLatestMetrics() + self.latest_metrics.append(temp_model.from_map(k)) + if m.get('LatestProgress') is not None: + temp_model = GetTrainingJobResponseBodyLatestProgress() + self.latest_progress = temp_model.from_map(m['LatestProgress']) + self.output_channels = [] + if m.get('OutputChannels') is not None: + for k in m.get('OutputChannels'): + temp_model = GetTrainingJobResponseBodyOutputChannels() + self.output_channels.append(temp_model.from_map(k)) + if m.get('OutputModel') is not None: + temp_model = GetTrainingJobResponseBodyOutputModel() + self.output_model = temp_model.from_map(m['OutputModel']) + if m.get('PythonRequirements') is not None: + self.python_requirements = m.get('PythonRequirements') + if m.get('ReasonCode') is not None: + self.reason_code = m.get('ReasonCode') + if m.get('ReasonMessage') is not None: + self.reason_message = m.get('ReasonMessage') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('RoleArn') is not None: + self.role_arn = m.get('RoleArn') + if m.get('Scheduler') is not None: + temp_model = GetTrainingJobResponseBodyScheduler() + self.scheduler = temp_model.from_map(m['Scheduler']) + if m.get('Settings') is not None: + temp_model = GetTrainingJobResponseBodySettings() + self.settings = temp_model.from_map(m['Settings']) + if m.get('Status') is not None: + self.status = m.get('Status') + self.status_transitions = [] + if m.get('StatusTransitions') is not None: + for k in m.get('StatusTransitions'): + temp_model = GetTrainingJobResponseBodyStatusTransitions() + self.status_transitions.append(temp_model.from_map(k)) + if m.get('TrainingJobDescription') is not None: + self.training_job_description = m.get('TrainingJobDescription') + if m.get('TrainingJobId') is not None: + self.training_job_id = m.get('TrainingJobId') + if m.get('TrainingJobName') is not None: + self.training_job_name = m.get('TrainingJobName') + if m.get('TrainingJobUrl') is not None: + self.training_job_url = m.get('TrainingJobUrl') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + if m.get('UserVpc') is not None: + temp_model = GetTrainingJobResponseBodyUserVpc() + self.user_vpc = temp_model.from_map(m['UserVpc']) + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') + return self + + +class GetTrainingJobResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetTrainingJobResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetTrainingJobResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetTrainingJobErrorInfoRequest(TeaModel): + def __init__( + self, + token: str = None, + ): + self.token = token + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.token is not None: + result['Token'] = self.token + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Token') is not None: + self.token = m.get('Token') + return self + + +class GetTrainingJobErrorInfoResponseBodyErrorInfo(TeaModel): + def __init__( + self, + additional_info: str = None, + code: str = None, + message: str = None, + ): + self.additional_info = additional_info + self.code = code + self.message = message + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.additional_info is not None: + result['AdditionalInfo'] = self.additional_info + if self.code is not None: + result['Code'] = self.code + if self.message is not None: + result['Message'] = self.message + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('AdditionalInfo') is not None: + self.additional_info = m.get('AdditionalInfo') + if m.get('Code') is not None: + self.code = m.get('Code') + if m.get('Message') is not None: + self.message = m.get('Message') + return self + + +class GetTrainingJobErrorInfoResponseBody(TeaModel): + def __init__( + self, + error_info: GetTrainingJobErrorInfoResponseBodyErrorInfo = None, + request_id: str = None, + ): + self.error_info = error_info + self.request_id = request_id + + def validate(self): + if self.error_info: + self.error_info.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.error_info is not None: + result['ErrorInfo'] = self.error_info.to_map() + if self.request_id is not None: + result['RequestId'] = self.request_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('ErrorInfo') is not None: + temp_model = GetTrainingJobErrorInfoResponseBodyErrorInfo() + self.error_info = temp_model.from_map(m['ErrorInfo']) + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + return self + + +class GetTrainingJobErrorInfoResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetTrainingJobErrorInfoResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetTrainingJobErrorInfoResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetTrainingJobLatestMetricsRequest(TeaModel): + def __init__( + self, + names: str = None, + token: str = None, + ): + self.names = names + self.token = token + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.names is not None: + result['Names'] = self.names + if self.token is not None: + result['Token'] = self.token + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Names') is not None: + self.names = m.get('Names') + if m.get('Token') is not None: + self.token = m.get('Token') + return self + + +class GetTrainingJobLatestMetricsResponseBodyMetrics(TeaModel): + def __init__( + self, + name: str = None, + timestamp: str = None, + value: float = None, + ): + self.name = name + self.timestamp = timestamp + self.value = value + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.name is not None: + result['Name'] = self.name + if self.timestamp is not None: + result['Timestamp'] = self.timestamp + if self.value is not None: + result['Value'] = self.value + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('Timestamp') is not None: + self.timestamp = m.get('Timestamp') + if m.get('Value') is not None: + self.value = m.get('Value') + return self + + +class GetTrainingJobLatestMetricsResponseBody(TeaModel): + def __init__( + self, + metrics: List[GetTrainingJobLatestMetricsResponseBodyMetrics] = None, + request_id: str = None, + ): + self.metrics = metrics + self.request_id = request_id + + def validate(self): + if self.metrics: + for k in self.metrics: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + result['Metrics'] = [] + if self.metrics is not None: + for k in self.metrics: + result['Metrics'].append(k.to_map() if k else None) + if self.request_id is not None: + result['RequestId'] = self.request_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + self.metrics = [] + if m.get('Metrics') is not None: + for k in m.get('Metrics'): + temp_model = GetTrainingJobLatestMetricsResponseBodyMetrics() + self.metrics.append(temp_model.from_map(k)) + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + return self + + +class GetTrainingJobLatestMetricsResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetTrainingJobLatestMetricsResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetTrainingJobLatestMetricsResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetUserViewMetricsRequest(TeaModel): + def __init__( + self, + order: str = None, + page_number: str = None, + page_size: str = None, + sort_by: str = None, + time_step: str = None, + user_id: str = None, + workspace_id: str = None, + ): + self.order = order + self.page_number = page_number + self.page_size = page_size + self.sort_by = sort_by + self.time_step = time_step + self.user_id = user_id + self.workspace_id = workspace_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.order is not None: + result['Order'] = self.order + if self.page_number is not None: + result['PageNumber'] = self.page_number + if self.page_size is not None: + result['PageSize'] = self.page_size + if self.sort_by is not None: + result['SortBy'] = self.sort_by + if self.time_step is not None: + result['TimeStep'] = self.time_step + if self.user_id is not None: + result['UserId'] = self.user_id + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Order') is not None: + self.order = m.get('Order') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + if m.get('SortBy') is not None: + self.sort_by = m.get('SortBy') + if m.get('TimeStep') is not None: + self.time_step = m.get('TimeStep') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') + return self + + +class GetUserViewMetricsResponseBody(TeaModel): + def __init__( + self, + resource_group_id: str = None, + summary: UserViewMetric = None, + total: int = None, + user_metrics: List[UserViewMetric] = None, + ): + self.resource_group_id = resource_group_id + self.summary = summary + self.total = total + self.user_metrics = user_metrics + + def validate(self): + if self.summary: + self.summary.validate() + if self.user_metrics: + for k in self.user_metrics: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.resource_group_id is not None: + result['ResourceGroupId'] = self.resource_group_id + if self.summary is not None: + result['Summary'] = self.summary.to_map() + if self.total is not None: + result['Total'] = self.total + result['UserMetrics'] = [] + if self.user_metrics is not None: + for k in self.user_metrics: + result['UserMetrics'].append(k.to_map() if k else None) + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('ResourceGroupId') is not None: + self.resource_group_id = m.get('ResourceGroupId') + if m.get('Summary') is not None: + temp_model = UserViewMetric() + self.summary = temp_model.from_map(m['Summary']) + if m.get('Total') is not None: + self.total = m.get('Total') + self.user_metrics = [] + if m.get('UserMetrics') is not None: + for k in m.get('UserMetrics'): + temp_model = UserViewMetric() + self.user_metrics.append(temp_model.from_map(k)) + return self + + +class GetUserViewMetricsResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetUserViewMetricsResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetUserViewMetricsResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class ListAI4DSerivcesRequest(TeaModel): + def __init__( + self, + service_type: str = None, + workspace_id: str = None, + ): + self.service_type = service_type + self.workspace_id = workspace_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.service_type is not None: + result['ServiceType'] = self.service_type + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('ServiceType') is not None: + self.service_type = m.get('ServiceType') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') + return self + + +class ListAI4DSerivcesResponseBodyServices(TeaModel): + def __init__( + self, + service_name: str = None, + service_type: str = None, + ): + self.service_name = service_name + self.service_type = service_type + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.service_name is not None: + result['ServiceName'] = self.service_name + if self.service_type is not None: + result['ServiceType'] = self.service_type + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('ServiceName') is not None: + self.service_name = m.get('ServiceName') + if m.get('ServiceType') is not None: + self.service_type = m.get('ServiceType') + return self + + +class ListAI4DSerivcesResponseBody(TeaModel): + def __init__( + self, + request_id: str = None, + services: List[ListAI4DSerivcesResponseBodyServices] = None, + ): + self.request_id = request_id + self.services = services + + def validate(self): + if self.services: + for k in self.services: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.request_id is not None: + result['RequestId'] = self.request_id + result['Services'] = [] + if self.services is not None: + for k in self.services: + result['Services'].append(k.to_map() if k else None) + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + self.services = [] + if m.get('Services') is not None: + for k in m.get('Services'): + temp_model = ListAI4DSerivcesResponseBodyServices() + self.services.append(temp_model.from_map(k)) + return self + + +class ListAI4DSerivcesResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: ListAI4DSerivcesResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = ListAI4DSerivcesResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class ListAI4DServiceTemplatesRequest(TeaModel): + def __init__( + self, + service_type: str = None, + workspace_id: str = None, + ): + self.service_type = service_type + self.workspace_id = workspace_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.service_type is not None: + result['ServiceType'] = self.service_type + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('ServiceType') is not None: + self.service_type = m.get('ServiceType') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') + return self + + +class ListAI4DServiceTemplatesResponseBodyServiceTemplatesLabels(TeaModel): + def __init__( + self, + key: str = None, + value: str = None, + ): + self.key = key + self.value = value + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.key is not None: + result['Key'] = self.key + if self.value is not None: + result['Value'] = self.value + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Key') is not None: + self.key = m.get('Key') + if m.get('Value') is not None: + self.value = m.get('Value') + return self + + +class ListAI4DServiceTemplatesResponseBodyServiceTemplates(TeaModel): + def __init__( + self, + inference_spec: Dict[str, Any] = None, + labels: List[ListAI4DServiceTemplatesResponseBodyServiceTemplatesLabels] = None, + service_template_description: str = None, + service_template_doc: str = None, + service_template_id: str = None, + service_template_name: str = None, + ): + self.inference_spec = inference_spec + self.labels = labels + self.service_template_description = service_template_description + self.service_template_doc = service_template_doc + self.service_template_id = service_template_id + self.service_template_name = service_template_name + + def validate(self): + if self.labels: + for k in self.labels: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.inference_spec is not None: + result['InferenceSpec'] = self.inference_spec + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + if self.service_template_description is not None: + result['ServiceTemplateDescription'] = self.service_template_description + if self.service_template_doc is not None: + result['ServiceTemplateDoc'] = self.service_template_doc + if self.service_template_id is not None: + result['ServiceTemplateId'] = self.service_template_id + if self.service_template_name is not None: + result['ServiceTemplateName'] = self.service_template_name + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('InferenceSpec') is not None: + self.inference_spec = m.get('InferenceSpec') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = ListAI4DServiceTemplatesResponseBodyServiceTemplatesLabels() + self.labels.append(temp_model.from_map(k)) + if m.get('ServiceTemplateDescription') is not None: + self.service_template_description = m.get('ServiceTemplateDescription') + if m.get('ServiceTemplateDoc') is not None: + self.service_template_doc = m.get('ServiceTemplateDoc') + if m.get('ServiceTemplateId') is not None: + self.service_template_id = m.get('ServiceTemplateId') + if m.get('ServiceTemplateName') is not None: + self.service_template_name = m.get('ServiceTemplateName') + return self + + +class ListAI4DServiceTemplatesResponseBody(TeaModel): + def __init__( + self, + request_id: str = None, + service_templates: List[ListAI4DServiceTemplatesResponseBodyServiceTemplates] = None, + ): self.request_id = request_id - self.role_arn = role_arn - self.scheduler = scheduler - self.status = status - self.status_transitions = status_transitions - self.training_job_description = training_job_description - self.training_job_id = training_job_id - self.training_job_name = training_job_name - self.training_job_url = training_job_url + self.service_templates = service_templates + + def validate(self): + if self.service_templates: + for k in self.service_templates: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.request_id is not None: + result['RequestId'] = self.request_id + result['ServiceTemplates'] = [] + if self.service_templates is not None: + for k in self.service_templates: + result['ServiceTemplates'].append(k.to_map() if k else None) + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + self.service_templates = [] + if m.get('ServiceTemplates') is not None: + for k in m.get('ServiceTemplates'): + temp_model = ListAI4DServiceTemplatesResponseBodyServiceTemplates() + self.service_templates.append(temp_model.from_map(k)) + return self + + +class ListAI4DServiceTemplatesResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: ListAI4DServiceTemplatesResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = ListAI4DServiceTemplatesResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class ListAlgorithmVersionsRequest(TeaModel): + def __init__( + self, + page_number: int = None, + page_size: int = None, + ): + self.page_number = page_number + self.page_size = page_size + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.page_number is not None: + result['PageNumber'] = self.page_number + if self.page_size is not None: + result['PageSize'] = self.page_size + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + return self + + +class ListAlgorithmVersionsResponseBodyAlgorithmVersions(TeaModel): + def __init__( + self, + algorithm_id: str = None, + algorithm_name: str = None, + algorithm_provider: str = None, + algorithm_version: str = None, + gmt_create_time: str = None, + gmt_modified_time: str = None, + tenant_id: str = None, + user_id: str = None, + ): + self.algorithm_id = algorithm_id + self.algorithm_name = algorithm_name + self.algorithm_provider = algorithm_provider + self.algorithm_version = algorithm_version + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time + self.tenant_id = tenant_id self.user_id = user_id - self.user_vpc = user_vpc - self.workspace_id = workspace_id def validate(self): - if self.algorithm_spec: - self.algorithm_spec.validate() - if self.compute_resource: - self.compute_resource.validate() - if self.hyper_parameters: - for k in self.hyper_parameters: - if k: - k.validate() - if self.input_channels: - for k in self.input_channels: - if k: - k.validate() - if self.instances: - for k in self.instances: - if k: - k.validate() - if self.labels: - for k in self.labels: - if k: - k.validate() - if self.latest_metrics: - for k in self.latest_metrics: - if k: - k.validate() - if self.latest_progress: - self.latest_progress.validate() - if self.output_channels: - for k in self.output_channels: - if k: - k.validate() - if self.scheduler: - self.scheduler.validate() - if self.status_transitions: - for k in self.status_transitions: - if k: - k.validate() - if self.user_vpc: - self.user_vpc.validate() + pass def to_map(self): _map = super().to_map() @@ -10409,182 +15445,98 @@ def to_map(self): result['AlgorithmName'] = self.algorithm_name if self.algorithm_provider is not None: result['AlgorithmProvider'] = self.algorithm_provider - if self.algorithm_spec is not None: - result['AlgorithmSpec'] = self.algorithm_spec.to_map() if self.algorithm_version is not None: result['AlgorithmVersion'] = self.algorithm_version - if self.compute_resource is not None: - result['ComputeResource'] = self.compute_resource.to_map() if self.gmt_create_time is not None: result['GmtCreateTime'] = self.gmt_create_time if self.gmt_modified_time is not None: result['GmtModifiedTime'] = self.gmt_modified_time - result['HyperParameters'] = [] - if self.hyper_parameters is not None: - for k in self.hyper_parameters: - result['HyperParameters'].append(k.to_map() if k else None) - result['InputChannels'] = [] - if self.input_channels is not None: - for k in self.input_channels: - result['InputChannels'].append(k.to_map() if k else None) - result['Instances'] = [] - if self.instances is not None: - for k in self.instances: - result['Instances'].append(k.to_map() if k else None) - if self.is_temp_algo is not None: - result['IsTempAlgo'] = self.is_temp_algo - result['Labels'] = [] - if self.labels is not None: - for k in self.labels: - result['Labels'].append(k.to_map() if k else None) - result['LatestMetrics'] = [] - if self.latest_metrics is not None: - for k in self.latest_metrics: - result['LatestMetrics'].append(k.to_map() if k else None) - if self.latest_progress is not None: - result['LatestProgress'] = self.latest_progress.to_map() - result['OutputChannels'] = [] - if self.output_channels is not None: - for k in self.output_channels: - result['OutputChannels'].append(k.to_map() if k else None) - if self.reason_code is not None: - result['ReasonCode'] = self.reason_code - if self.reason_message is not None: - result['ReasonMessage'] = self.reason_message - if self.request_id is not None: - result['RequestId'] = self.request_id - if self.role_arn is not None: - result['RoleArn'] = self.role_arn - if self.scheduler is not None: - result['Scheduler'] = self.scheduler.to_map() - if self.status is not None: - result['Status'] = self.status - result['StatusTransitions'] = [] - if self.status_transitions is not None: - for k in self.status_transitions: - result['StatusTransitions'].append(k.to_map() if k else None) - if self.training_job_description is not None: - result['TrainingJobDescription'] = self.training_job_description - if self.training_job_id is not None: - result['TrainingJobId'] = self.training_job_id - if self.training_job_name is not None: - result['TrainingJobName'] = self.training_job_name - if self.training_job_url is not None: - result['TrainingJobUrl'] = self.training_job_url + if self.tenant_id is not None: + result['TenantId'] = self.tenant_id if self.user_id is not None: result['UserId'] = self.user_id - if self.user_vpc is not None: - result['UserVpc'] = self.user_vpc.to_map() - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('AlgorithmId') is not None: - self.algorithm_id = m.get('AlgorithmId') - if m.get('AlgorithmName') is not None: - self.algorithm_name = m.get('AlgorithmName') - if m.get('AlgorithmProvider') is not None: - self.algorithm_provider = m.get('AlgorithmProvider') - if m.get('AlgorithmSpec') is not None: - temp_model = AlgorithmSpec() - self.algorithm_spec = temp_model.from_map(m['AlgorithmSpec']) - if m.get('AlgorithmVersion') is not None: - self.algorithm_version = m.get('AlgorithmVersion') - if m.get('ComputeResource') is not None: - temp_model = GetTrainingJobResponseBodyComputeResource() - self.compute_resource = temp_model.from_map(m['ComputeResource']) - if m.get('GmtCreateTime') is not None: - self.gmt_create_time = m.get('GmtCreateTime') - if m.get('GmtModifiedTime') is not None: - self.gmt_modified_time = m.get('GmtModifiedTime') - self.hyper_parameters = [] - if m.get('HyperParameters') is not None: - for k in m.get('HyperParameters'): - temp_model = GetTrainingJobResponseBodyHyperParameters() - self.hyper_parameters.append(temp_model.from_map(k)) - self.input_channels = [] - if m.get('InputChannels') is not None: - for k in m.get('InputChannels'): - temp_model = GetTrainingJobResponseBodyInputChannels() - self.input_channels.append(temp_model.from_map(k)) - self.instances = [] - if m.get('Instances') is not None: - for k in m.get('Instances'): - temp_model = GetTrainingJobResponseBodyInstances() - self.instances.append(temp_model.from_map(k)) - if m.get('IsTempAlgo') is not None: - self.is_temp_algo = m.get('IsTempAlgo') - self.labels = [] - if m.get('Labels') is not None: - for k in m.get('Labels'): - temp_model = GetTrainingJobResponseBodyLabels() - self.labels.append(temp_model.from_map(k)) - self.latest_metrics = [] - if m.get('LatestMetrics') is not None: - for k in m.get('LatestMetrics'): - temp_model = GetTrainingJobResponseBodyLatestMetrics() - self.latest_metrics.append(temp_model.from_map(k)) - if m.get('LatestProgress') is not None: - temp_model = GetTrainingJobResponseBodyLatestProgress() - self.latest_progress = temp_model.from_map(m['LatestProgress']) - self.output_channels = [] - if m.get('OutputChannels') is not None: - for k in m.get('OutputChannels'): - temp_model = GetTrainingJobResponseBodyOutputChannels() - self.output_channels.append(temp_model.from_map(k)) - if m.get('ReasonCode') is not None: - self.reason_code = m.get('ReasonCode') - if m.get('ReasonMessage') is not None: - self.reason_message = m.get('ReasonMessage') + if m.get('AlgorithmId') is not None: + self.algorithm_id = m.get('AlgorithmId') + if m.get('AlgorithmName') is not None: + self.algorithm_name = m.get('AlgorithmName') + if m.get('AlgorithmProvider') is not None: + self.algorithm_provider = m.get('AlgorithmProvider') + if m.get('AlgorithmVersion') is not None: + self.algorithm_version = m.get('AlgorithmVersion') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + if m.get('TenantId') is not None: + self.tenant_id = m.get('TenantId') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + return self + + +class ListAlgorithmVersionsResponseBody(TeaModel): + def __init__( + self, + algorithm_versions: List[ListAlgorithmVersionsResponseBodyAlgorithmVersions] = None, + request_id: str = None, + total_count: int = None, + ): + self.algorithm_versions = algorithm_versions + self.request_id = request_id + self.total_count = total_count + + def validate(self): + if self.algorithm_versions: + for k in self.algorithm_versions: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + result['AlgorithmVersions'] = [] + if self.algorithm_versions is not None: + for k in self.algorithm_versions: + result['AlgorithmVersions'].append(k.to_map() if k else None) + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.total_count is not None: + result['TotalCount'] = self.total_count + return result + + def from_map(self, m: dict = None): + m = m or dict() + self.algorithm_versions = [] + if m.get('AlgorithmVersions') is not None: + for k in m.get('AlgorithmVersions'): + temp_model = ListAlgorithmVersionsResponseBodyAlgorithmVersions() + self.algorithm_versions.append(temp_model.from_map(k)) if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('RoleArn') is not None: - self.role_arn = m.get('RoleArn') - if m.get('Scheduler') is not None: - temp_model = GetTrainingJobResponseBodyScheduler() - self.scheduler = temp_model.from_map(m['Scheduler']) - if m.get('Status') is not None: - self.status = m.get('Status') - self.status_transitions = [] - if m.get('StatusTransitions') is not None: - for k in m.get('StatusTransitions'): - temp_model = GetTrainingJobResponseBodyStatusTransitions() - self.status_transitions.append(temp_model.from_map(k)) - if m.get('TrainingJobDescription') is not None: - self.training_job_description = m.get('TrainingJobDescription') - if m.get('TrainingJobId') is not None: - self.training_job_id = m.get('TrainingJobId') - if m.get('TrainingJobName') is not None: - self.training_job_name = m.get('TrainingJobName') - if m.get('TrainingJobUrl') is not None: - self.training_job_url = m.get('TrainingJobUrl') - if m.get('UserId') is not None: - self.user_id = m.get('UserId') - if m.get('UserVpc') is not None: - temp_model = GetTrainingJobResponseBodyUserVpc() - self.user_vpc = temp_model.from_map(m['UserVpc']) - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') return self -class GetTrainingJobResponse(TeaModel): +class ListAlgorithmVersionsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetTrainingJobResponseBody = None, + body: ListAlgorithmVersionsResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -10609,19 +15561,27 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetTrainingJobResponseBody() + temp_model = ListAlgorithmVersionsResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetTrainingJobLatestMetricsRequest(TeaModel): +class ListAlgorithmsRequest(TeaModel): def __init__( self, - names: str = None, - token: str = None, + algorithm_id: str = None, + algorithm_name: str = None, + algorithm_provider: str = None, + page_number: int = None, + page_size: int = None, + workspace_id: str = None, ): - self.names = names - self.token = token + self.algorithm_id = algorithm_id + self.algorithm_name = algorithm_name + self.algorithm_provider = algorithm_provider + self.page_number = page_number + self.page_size = page_size + self.workspace_id = workspace_id def validate(self): pass @@ -10632,31 +15592,59 @@ def to_map(self): return _map result = dict() - if self.names is not None: - result['Names'] = self.names - if self.token is not None: - result['Token'] = self.token + if self.algorithm_id is not None: + result['AlgorithmId'] = self.algorithm_id + if self.algorithm_name is not None: + result['AlgorithmName'] = self.algorithm_name + if self.algorithm_provider is not None: + result['AlgorithmProvider'] = self.algorithm_provider + if self.page_number is not None: + result['PageNumber'] = self.page_number + if self.page_size is not None: + result['PageSize'] = self.page_size + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Names') is not None: - self.names = m.get('Names') - if m.get('Token') is not None: - self.token = m.get('Token') + if m.get('AlgorithmId') is not None: + self.algorithm_id = m.get('AlgorithmId') + if m.get('AlgorithmName') is not None: + self.algorithm_name = m.get('AlgorithmName') + if m.get('AlgorithmProvider') is not None: + self.algorithm_provider = m.get('AlgorithmProvider') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class GetTrainingJobLatestMetricsResponseBodyMetrics(TeaModel): +class ListAlgorithmsResponseBodyAlgorithms(TeaModel): def __init__( self, - name: str = None, - timestamp: str = None, - value: float = None, + algorithm_description: str = None, + algorithm_id: str = None, + algorithm_name: str = None, + algorithm_provider: str = None, + display_name: str = None, + gmt_create_time: str = None, + gmt_modified_time: str = None, + user_id: str = None, + workspace_id: str = None, ): - self.name = name - self.timestamp = timestamp - self.value = value + self.algorithm_description = algorithm_description + self.algorithm_id = algorithm_id + self.algorithm_name = algorithm_name + self.algorithm_provider = algorithm_provider + self.display_name = display_name + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time + self.user_id = user_id + self.workspace_id = workspace_id def validate(self): pass @@ -10667,37 +15655,63 @@ def to_map(self): return _map result = dict() - if self.name is not None: - result['Name'] = self.name - if self.timestamp is not None: - result['Timestamp'] = self.timestamp - if self.value is not None: - result['Value'] = self.value + if self.algorithm_description is not None: + result['AlgorithmDescription'] = self.algorithm_description + if self.algorithm_id is not None: + result['AlgorithmId'] = self.algorithm_id + if self.algorithm_name is not None: + result['AlgorithmName'] = self.algorithm_name + if self.algorithm_provider is not None: + result['AlgorithmProvider'] = self.algorithm_provider + if self.display_name is not None: + result['DisplayName'] = self.display_name + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + if self.user_id is not None: + result['UserId'] = self.user_id + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Name') is not None: - self.name = m.get('Name') - if m.get('Timestamp') is not None: - self.timestamp = m.get('Timestamp') - if m.get('Value') is not None: - self.value = m.get('Value') + if m.get('AlgorithmDescription') is not None: + self.algorithm_description = m.get('AlgorithmDescription') + if m.get('AlgorithmId') is not None: + self.algorithm_id = m.get('AlgorithmId') + if m.get('AlgorithmName') is not None: + self.algorithm_name = m.get('AlgorithmName') + if m.get('AlgorithmProvider') is not None: + self.algorithm_provider = m.get('AlgorithmProvider') + if m.get('DisplayName') is not None: + self.display_name = m.get('DisplayName') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class GetTrainingJobLatestMetricsResponseBody(TeaModel): +class ListAlgorithmsResponseBody(TeaModel): def __init__( self, - metrics: List[GetTrainingJobLatestMetricsResponseBodyMetrics] = None, + algorithms: List[ListAlgorithmsResponseBodyAlgorithms] = None, request_id: str = None, + total_count: int = None, ): - self.metrics = metrics + self.algorithms = algorithms self.request_id = request_id + self.total_count = total_count def validate(self): - if self.metrics: - for k in self.metrics: + if self.algorithms: + for k in self.algorithms: if k: k.validate() @@ -10707,41 +15721,42 @@ def to_map(self): return _map result = dict() - result['Metrics'] = [] - if self.metrics is not None: - for k in self.metrics: - result['Metrics'].append(k.to_map() if k else None) + result['Algorithms'] = [] + if self.algorithms is not None: + for k in self.algorithms: + result['Algorithms'].append(k.to_map() if k else None) if self.request_id is not None: result['RequestId'] = self.request_id + if self.total_count is not None: + result['TotalCount'] = self.total_count return result def from_map(self, m: dict = None): - m = m or dict() - self.metrics = [] - if m.get('Metrics') is not None: - for k in m.get('Metrics'): - temp_model = GetTrainingJobLatestMetricsResponseBodyMetrics() - self.metrics.append(temp_model.from_map(k)) + m = m or dict() + self.algorithms = [] + if m.get('Algorithms') is not None: + for k in m.get('Algorithms'): + temp_model = ListAlgorithmsResponseBodyAlgorithms() + self.algorithms.append(temp_model.from_map(k)) if m.get('RequestId') is not None: self.request_id = m.get('RequestId') + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') return self -class GetTrainingJobLatestMetricsResponse(TeaModel): +class ListAlgorithmsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetTrainingJobLatestMetricsResponseBody = None, + body: ListAlgorithmsResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -10766,29 +15781,29 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetTrainingJobLatestMetricsResponseBody() + temp_model = ListAlgorithmsResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetUserViewMetricsRequest(TeaModel): +class ListComponentVersionSnapshotsRequest(TeaModel): def __init__( self, + component_id: str = None, order: str = None, - page_number: str = None, - page_size: str = None, + page_number: int = None, + page_size: int = None, + snapshot_id: str = None, sort_by: str = None, - time_step: str = None, - user_id: str = None, - workspace_id: str = None, + version: str = None, ): + self.component_id = component_id self.order = order self.page_number = page_number self.page_size = page_size + self.snapshot_id = snapshot_id self.sort_by = sort_by - self.time_step = time_step - self.user_id = user_id - self.workspace_id = workspace_id + self.version = version def validate(self): pass @@ -10799,59 +15814,124 @@ def to_map(self): return _map result = dict() + if self.component_id is not None: + result['ComponentId'] = self.component_id if self.order is not None: result['Order'] = self.order if self.page_number is not None: result['PageNumber'] = self.page_number if self.page_size is not None: result['PageSize'] = self.page_size + if self.snapshot_id is not None: + result['SnapshotId'] = self.snapshot_id if self.sort_by is not None: result['SortBy'] = self.sort_by - if self.time_step is not None: - result['TimeStep'] = self.time_step - if self.user_id is not None: - result['UserId'] = self.user_id - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id + if self.version is not None: + result['Version'] = self.version return result def from_map(self, m: dict = None): m = m or dict() + if m.get('ComponentId') is not None: + self.component_id = m.get('ComponentId') if m.get('Order') is not None: self.order = m.get('Order') if m.get('PageNumber') is not None: self.page_number = m.get('PageNumber') if m.get('PageSize') is not None: self.page_size = m.get('PageSize') + if m.get('SnapshotId') is not None: + self.snapshot_id = m.get('SnapshotId') if m.get('SortBy') is not None: self.sort_by = m.get('SortBy') - if m.get('TimeStep') is not None: - self.time_step = m.get('TimeStep') + if m.get('Version') is not None: + self.version = m.get('Version') + return self + + +class ListComponentVersionSnapshotsResponseBodySnapshots(TeaModel): + def __init__( + self, + component_id: str = None, + description: str = None, + is_current_version: bool = None, + snapshot_id: str = None, + tenant_id: str = None, + user_id: str = None, + version: str = None, + workspace_id: str = None, + ): + self.component_id = component_id + self.description = description + self.is_current_version = is_current_version + self.snapshot_id = snapshot_id + self.tenant_id = tenant_id + self.user_id = user_id + self.version = version + self.workspace_id = workspace_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.component_id is not None: + result['ComponentId'] = self.component_id + if self.description is not None: + result['Description'] = self.description + if self.is_current_version is not None: + result['IsCurrentVersion'] = self.is_current_version + if self.snapshot_id is not None: + result['SnapshotId'] = self.snapshot_id + if self.tenant_id is not None: + result['TenantId'] = self.tenant_id + if self.user_id is not None: + result['UserId'] = self.user_id + if self.version is not None: + result['Version'] = self.version + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('ComponentId') is not None: + self.component_id = m.get('ComponentId') + if m.get('Description') is not None: + self.description = m.get('Description') + if m.get('IsCurrentVersion') is not None: + self.is_current_version = m.get('IsCurrentVersion') + if m.get('SnapshotId') is not None: + self.snapshot_id = m.get('SnapshotId') + if m.get('TenantId') is not None: + self.tenant_id = m.get('TenantId') if m.get('UserId') is not None: self.user_id = m.get('UserId') + if m.get('Version') is not None: + self.version = m.get('Version') if m.get('WorkspaceId') is not None: self.workspace_id = m.get('WorkspaceId') return self -class GetUserViewMetricsResponseBody(TeaModel): +class ListComponentVersionSnapshotsResponseBody(TeaModel): def __init__( self, - resource_group_id: str = None, - summary: UserViewMetric = None, - total: int = None, - user_metrics: List[UserViewMetric] = None, + request_id: str = None, + snapshots: List[ListComponentVersionSnapshotsResponseBodySnapshots] = None, + total_count: int = None, ): - self.resource_group_id = resource_group_id - self.summary = summary - self.total = total - self.user_metrics = user_metrics + self.request_id = request_id + self.snapshots = snapshots + self.total_count = total_count def validate(self): - if self.summary: - self.summary.validate() - if self.user_metrics: - for k in self.user_metrics: + if self.snapshots: + for k in self.snapshots: if k: k.validate() @@ -10861,50 +15941,42 @@ def to_map(self): return _map result = dict() - if self.resource_group_id is not None: - result['ResourceGroupId'] = self.resource_group_id - if self.summary is not None: - result['Summary'] = self.summary.to_map() - if self.total is not None: - result['Total'] = self.total - result['UserMetrics'] = [] - if self.user_metrics is not None: - for k in self.user_metrics: - result['UserMetrics'].append(k.to_map() if k else None) + if self.request_id is not None: + result['RequestId'] = self.request_id + result['Snapshots'] = [] + if self.snapshots is not None: + for k in self.snapshots: + result['Snapshots'].append(k.to_map() if k else None) + if self.total_count is not None: + result['TotalCount'] = self.total_count return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ResourceGroupId') is not None: - self.resource_group_id = m.get('ResourceGroupId') - if m.get('Summary') is not None: - temp_model = UserViewMetric() - self.summary = temp_model.from_map(m['Summary']) - if m.get('Total') is not None: - self.total = m.get('Total') - self.user_metrics = [] - if m.get('UserMetrics') is not None: - for k in m.get('UserMetrics'): - temp_model = UserViewMetric() - self.user_metrics.append(temp_model.from_map(k)) + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + self.snapshots = [] + if m.get('Snapshots') is not None: + for k in m.get('Snapshots'): + temp_model = ListComponentVersionSnapshotsResponseBodySnapshots() + self.snapshots.append(temp_model.from_map(k)) + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') return self -class GetUserViewMetricsResponse(TeaModel): +class ListComponentVersionSnapshotsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetUserViewMetricsResponseBody = None, + body: ListComponentVersionSnapshotsResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -10929,19 +16001,84 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetUserViewMetricsResponseBody() + temp_model = ListComponentVersionSnapshotsResponseBody() self.body = temp_model.from_map(m['body']) return self -class ListAI4DSerivcesRequest(TeaModel): +class ListComponentVersionsRequest(TeaModel): def __init__( self, - service_type: str = None, - workspace_id: str = None, + labels: Dict[str, str] = None, + order: str = None, + page_number: int = None, + page_size: int = None, + sort_by: str = None, + version: str = None, ): - self.service_type = service_type - self.workspace_id = workspace_id + self.labels = labels + self.order = order + self.page_number = page_number + self.page_size = page_size + self.sort_by = sort_by + self.version = version + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.labels is not None: + result['Labels'] = self.labels + if self.order is not None: + result['Order'] = self.order + if self.page_number is not None: + result['PageNumber'] = self.page_number + if self.page_size is not None: + result['PageSize'] = self.page_size + if self.sort_by is not None: + result['SortBy'] = self.sort_by + if self.version is not None: + result['Version'] = self.version + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Labels') is not None: + self.labels = m.get('Labels') + if m.get('Order') is not None: + self.order = m.get('Order') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + if m.get('SortBy') is not None: + self.sort_by = m.get('SortBy') + if m.get('Version') is not None: + self.version = m.get('Version') + return self + + +class ListComponentVersionsShrinkRequest(TeaModel): + def __init__( + self, + labels_shrink: str = None, + order: str = None, + page_number: int = None, + page_size: int = None, + sort_by: str = None, + version: str = None, + ): + self.labels_shrink = labels_shrink + self.order = order + self.page_number = page_number + self.page_size = page_size + self.sort_by = sort_by + self.version = version def validate(self): pass @@ -10952,32 +16089,69 @@ def to_map(self): return _map result = dict() - if self.service_type is not None: - result['ServiceType'] = self.service_type - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id + if self.labels_shrink is not None: + result['Labels'] = self.labels_shrink + if self.order is not None: + result['Order'] = self.order + if self.page_number is not None: + result['PageNumber'] = self.page_number + if self.page_size is not None: + result['PageSize'] = self.page_size + if self.sort_by is not None: + result['SortBy'] = self.sort_by + if self.version is not None: + result['Version'] = self.version return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ServiceType') is not None: - self.service_type = m.get('ServiceType') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') + if m.get('Labels') is not None: + self.labels_shrink = m.get('Labels') + if m.get('Order') is not None: + self.order = m.get('Order') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + if m.get('SortBy') is not None: + self.sort_by = m.get('SortBy') + if m.get('Version') is not None: + self.version = m.get('Version') return self -class ListAI4DSerivcesResponseBodyServices(TeaModel): +class ListComponentVersionsResponseBodyComponentVersions(TeaModel): def __init__( self, - service_name: str = None, - service_type: str = None, + component_id: str = None, + gmt_create_time: str = None, + gmt_modified_time: str = None, + labels: List[Label] = None, + name: str = None, + provider: str = None, + status: str = None, + tenant_id: str = None, + user_id: str = None, + version: str = None, + workspace_id: str = None, ): - self.service_name = service_name - self.service_type = service_type + self.component_id = component_id + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time + self.labels = labels + self.name = name + self.provider = provider + self.status = status + self.tenant_id = tenant_id + self.user_id = user_id + self.version = version + self.workspace_id = workspace_id def validate(self): - pass + if self.labels: + for k in self.labels: + if k: + k.validate() def to_map(self): _map = super().to_map() @@ -10985,33 +16159,76 @@ def to_map(self): return _map result = dict() - if self.service_name is not None: - result['ServiceName'] = self.service_name - if self.service_type is not None: - result['ServiceType'] = self.service_type + if self.component_id is not None: + result['ComponentId'] = self.component_id + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + if self.name is not None: + result['Name'] = self.name + if self.provider is not None: + result['Provider'] = self.provider + if self.status is not None: + result['Status'] = self.status + if self.tenant_id is not None: + result['TenantId'] = self.tenant_id + if self.user_id is not None: + result['UserId'] = self.user_id + if self.version is not None: + result['Version'] = self.version + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ServiceName') is not None: - self.service_name = m.get('ServiceName') - if m.get('ServiceType') is not None: - self.service_type = m.get('ServiceType') + if m.get('ComponentId') is not None: + self.component_id = m.get('ComponentId') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = Label() + self.labels.append(temp_model.from_map(k)) + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('Provider') is not None: + self.provider = m.get('Provider') + if m.get('Status') is not None: + self.status = m.get('Status') + if m.get('TenantId') is not None: + self.tenant_id = m.get('TenantId') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + if m.get('Version') is not None: + self.version = m.get('Version') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class ListAI4DSerivcesResponseBody(TeaModel): +class ListComponentVersionsResponseBody(TeaModel): def __init__( self, + component_versions: List[ListComponentVersionsResponseBodyComponentVersions] = None, request_id: str = None, - services: List[ListAI4DSerivcesResponseBodyServices] = None, + total_count: int = None, ): + self.component_versions = component_versions self.request_id = request_id - self.services = services + self.total_count = total_count def validate(self): - if self.services: - for k in self.services: + if self.component_versions: + for k in self.component_versions: if k: k.validate() @@ -11021,41 +16238,42 @@ def to_map(self): return _map result = dict() + result['ComponentVersions'] = [] + if self.component_versions is not None: + for k in self.component_versions: + result['ComponentVersions'].append(k.to_map() if k else None) if self.request_id is not None: result['RequestId'] = self.request_id - result['Services'] = [] - if self.services is not None: - for k in self.services: - result['Services'].append(k.to_map() if k else None) + if self.total_count is not None: + result['TotalCount'] = self.total_count return result def from_map(self, m: dict = None): m = m or dict() + self.component_versions = [] + if m.get('ComponentVersions') is not None: + for k in m.get('ComponentVersions'): + temp_model = ListComponentVersionsResponseBodyComponentVersions() + self.component_versions.append(temp_model.from_map(k)) if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - self.services = [] - if m.get('Services') is not None: - for k in m.get('Services'): - temp_model = ListAI4DSerivcesResponseBodyServices() - self.services.append(temp_model.from_map(k)) + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') return self -class ListAI4DSerivcesResponse(TeaModel): +class ListComponentVersionsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListAI4DSerivcesResponseBody = None, + body: ListComponentVersionsResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -11080,19 +16298,116 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListAI4DSerivcesResponseBody() + temp_model = ListComponentVersionsResponseBody() self.body = temp_model.from_map(m['body']) return self -class ListAlgorithmVersionsRequest(TeaModel): +class ListComponentsRequest(TeaModel): + def __init__( + self, + component_id: str = None, + component_ids: str = None, + labels: Dict[str, Any] = None, + name: str = None, + order: str = None, + page_number: int = None, + page_size: int = None, + provider: str = None, + sort_by: str = None, + workspace_id: str = None, + ): + self.component_id = component_id + self.component_ids = component_ids + self.labels = labels + self.name = name + self.order = order + self.page_number = page_number + self.page_size = page_size + self.provider = provider + self.sort_by = sort_by + self.workspace_id = workspace_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.component_id is not None: + result['ComponentId'] = self.component_id + if self.component_ids is not None: + result['ComponentIds'] = self.component_ids + if self.labels is not None: + result['Labels'] = self.labels + if self.name is not None: + result['Name'] = self.name + if self.order is not None: + result['Order'] = self.order + if self.page_number is not None: + result['PageNumber'] = self.page_number + if self.page_size is not None: + result['PageSize'] = self.page_size + if self.provider is not None: + result['Provider'] = self.provider + if self.sort_by is not None: + result['SortBy'] = self.sort_by + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('ComponentId') is not None: + self.component_id = m.get('ComponentId') + if m.get('ComponentIds') is not None: + self.component_ids = m.get('ComponentIds') + if m.get('Labels') is not None: + self.labels = m.get('Labels') + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('Order') is not None: + self.order = m.get('Order') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + if m.get('Provider') is not None: + self.provider = m.get('Provider') + if m.get('SortBy') is not None: + self.sort_by = m.get('SortBy') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') + return self + + +class ListComponentsShrinkRequest(TeaModel): def __init__( self, + component_id: str = None, + component_ids: str = None, + labels_shrink: str = None, + name: str = None, + order: str = None, page_number: int = None, page_size: int = None, + provider: str = None, + sort_by: str = None, + workspace_id: str = None, ): + self.component_id = component_id + self.component_ids = component_ids + self.labels_shrink = labels_shrink + self.name = name + self.order = order self.page_number = page_number self.page_size = page_size + self.provider = provider + self.sort_by = sort_by + self.workspace_id = workspace_id def validate(self): pass @@ -11103,44 +16418,136 @@ def to_map(self): return _map result = dict() + if self.component_id is not None: + result['ComponentId'] = self.component_id + if self.component_ids is not None: + result['ComponentIds'] = self.component_ids + if self.labels_shrink is not None: + result['Labels'] = self.labels_shrink + if self.name is not None: + result['Name'] = self.name + if self.order is not None: + result['Order'] = self.order if self.page_number is not None: result['PageNumber'] = self.page_number if self.page_size is not None: result['PageSize'] = self.page_size + if self.provider is not None: + result['Provider'] = self.provider + if self.sort_by is not None: + result['SortBy'] = self.sort_by + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() + if m.get('ComponentId') is not None: + self.component_id = m.get('ComponentId') + if m.get('ComponentIds') is not None: + self.component_ids = m.get('ComponentIds') + if m.get('Labels') is not None: + self.labels_shrink = m.get('Labels') + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('Order') is not None: + self.order = m.get('Order') if m.get('PageNumber') is not None: self.page_number = m.get('PageNumber') if m.get('PageSize') is not None: self.page_size = m.get('PageSize') + if m.get('Provider') is not None: + self.provider = m.get('Provider') + if m.get('SortBy') is not None: + self.sort_by = m.get('SortBy') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class ListAlgorithmVersionsResponseBodyAlgorithmVersions(TeaModel): +class ListComponentsResponseBodyComponentsVersions(TeaModel): def __init__( self, - algorithm_id: str = None, - algorithm_name: str = None, - algorithm_provider: str = None, - algorithm_version: str = None, + gmt_create_time: str = None, + snapshot_id: str = None, + status: str = None, + version: str = None, + ): + self.gmt_create_time = gmt_create_time + self.snapshot_id = snapshot_id + self.status = status + self.version = version + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.snapshot_id is not None: + result['SnapshotId'] = self.snapshot_id + if self.status is not None: + result['Status'] = self.status + if self.version is not None: + result['Version'] = self.version + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('SnapshotId') is not None: + self.snapshot_id = m.get('SnapshotId') + if m.get('Status') is not None: + self.status = m.get('Status') + if m.get('Version') is not None: + self.version = m.get('Version') + return self + + +class ListComponentsResponseBodyComponents(TeaModel): + def __init__( + self, + component_id: str = None, + description: str = None, + display_name: str = None, gmt_create_time: str = None, gmt_modified_time: str = None, + labels: List[Label] = None, + name: str = None, + provider: str = None, tenant_id: str = None, user_id: str = None, + versions: List[ListComponentsResponseBodyComponentsVersions] = None, + workspace_id: str = None, ): - self.algorithm_id = algorithm_id - self.algorithm_name = algorithm_name - self.algorithm_provider = algorithm_provider - self.algorithm_version = algorithm_version + self.component_id = component_id + self.description = description + self.display_name = display_name self.gmt_create_time = gmt_create_time self.gmt_modified_time = gmt_modified_time + self.labels = labels + self.name = name + self.provider = provider self.tenant_id = tenant_id self.user_id = user_id + self.versions = versions + self.workspace_id = workspace_id def validate(self): - pass + if self.labels: + for k in self.labels: + if k: + k.validate() + if self.versions: + for k in self.versions: + if k: + k.validate() def to_map(self): _map = super().to_map() @@ -11148,59 +16555,85 @@ def to_map(self): return _map result = dict() - if self.algorithm_id is not None: - result['AlgorithmId'] = self.algorithm_id - if self.algorithm_name is not None: - result['AlgorithmName'] = self.algorithm_name - if self.algorithm_provider is not None: - result['AlgorithmProvider'] = self.algorithm_provider - if self.algorithm_version is not None: - result['AlgorithmVersion'] = self.algorithm_version + if self.component_id is not None: + result['ComponentId'] = self.component_id + if self.description is not None: + result['Description'] = self.description + if self.display_name is not None: + result['DisplayName'] = self.display_name if self.gmt_create_time is not None: result['GmtCreateTime'] = self.gmt_create_time if self.gmt_modified_time is not None: result['GmtModifiedTime'] = self.gmt_modified_time + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + if self.name is not None: + result['Name'] = self.name + if self.provider is not None: + result['Provider'] = self.provider if self.tenant_id is not None: result['TenantId'] = self.tenant_id if self.user_id is not None: result['UserId'] = self.user_id + result['Versions'] = [] + if self.versions is not None: + for k in self.versions: + result['Versions'].append(k.to_map() if k else None) + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('AlgorithmId') is not None: - self.algorithm_id = m.get('AlgorithmId') - if m.get('AlgorithmName') is not None: - self.algorithm_name = m.get('AlgorithmName') - if m.get('AlgorithmProvider') is not None: - self.algorithm_provider = m.get('AlgorithmProvider') - if m.get('AlgorithmVersion') is not None: - self.algorithm_version = m.get('AlgorithmVersion') + if m.get('ComponentId') is not None: + self.component_id = m.get('ComponentId') + if m.get('Description') is not None: + self.description = m.get('Description') + if m.get('DisplayName') is not None: + self.display_name = m.get('DisplayName') if m.get('GmtCreateTime') is not None: self.gmt_create_time = m.get('GmtCreateTime') if m.get('GmtModifiedTime') is not None: self.gmt_modified_time = m.get('GmtModifiedTime') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = Label() + self.labels.append(temp_model.from_map(k)) + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('Provider') is not None: + self.provider = m.get('Provider') if m.get('TenantId') is not None: self.tenant_id = m.get('TenantId') if m.get('UserId') is not None: self.user_id = m.get('UserId') + self.versions = [] + if m.get('Versions') is not None: + for k in m.get('Versions'): + temp_model = ListComponentsResponseBodyComponentsVersions() + self.versions.append(temp_model.from_map(k)) + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class ListAlgorithmVersionsResponseBody(TeaModel): +class ListComponentsResponseBody(TeaModel): def __init__( self, - algorithm_versions: List[ListAlgorithmVersionsResponseBodyAlgorithmVersions] = None, + components: List[ListComponentsResponseBodyComponents] = None, request_id: str = None, total_count: int = None, ): - self.algorithm_versions = algorithm_versions + self.components = components self.request_id = request_id self.total_count = total_count def validate(self): - if self.algorithm_versions: - for k in self.algorithm_versions: + if self.components: + for k in self.components: if k: k.validate() @@ -11210,10 +16643,10 @@ def to_map(self): return _map result = dict() - result['AlgorithmVersions'] = [] - if self.algorithm_versions is not None: - for k in self.algorithm_versions: - result['AlgorithmVersions'].append(k.to_map() if k else None) + result['Components'] = [] + if self.components is not None: + for k in self.components: + result['Components'].append(k.to_map() if k else None) if self.request_id is not None: result['RequestId'] = self.request_id if self.total_count is not None: @@ -11222,11 +16655,11 @@ def to_map(self): def from_map(self, m: dict = None): m = m or dict() - self.algorithm_versions = [] - if m.get('AlgorithmVersions') is not None: - for k in m.get('AlgorithmVersions'): - temp_model = ListAlgorithmVersionsResponseBodyAlgorithmVersions() - self.algorithm_versions.append(temp_model.from_map(k)) + self.components = [] + if m.get('Components') is not None: + for k in m.get('Components'): + temp_model = ListComponentsResponseBodyComponents() + self.components.append(temp_model.from_map(k)) if m.get('RequestId') is not None: self.request_id = m.get('RequestId') if m.get('TotalCount') is not None: @@ -11234,21 +16667,18 @@ def from_map(self, m: dict = None): return self -class ListAlgorithmVersionsResponse(TeaModel): +class ListComponentsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListAlgorithmVersionsResponseBody = None, + body: ListComponentsResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -11273,27 +16703,27 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListAlgorithmVersionsResponseBody() + temp_model = ListComponentsResponseBody() self.body = temp_model.from_map(m['body']) return self -class ListAlgorithmsRequest(TeaModel): +class ListInstanceJobsRequest(TeaModel): def __init__( self, - algorithm_id: str = None, - algorithm_name: str = None, - algorithm_provider: str = None, + instance_job_type: str = None, + order: str = None, page_number: int = None, page_size: int = None, - workspace_id: str = None, + sort_by: str = None, + status: str = None, ): - self.algorithm_id = algorithm_id - self.algorithm_name = algorithm_name - self.algorithm_provider = algorithm_provider + self.instance_job_type = instance_job_type + self.order = order self.page_number = page_number self.page_size = page_size - self.workspace_id = workspace_id + self.sort_by = sort_by + self.status = status def validate(self): pass @@ -11304,58 +16734,58 @@ def to_map(self): return _map result = dict() - if self.algorithm_id is not None: - result['AlgorithmId'] = self.algorithm_id - if self.algorithm_name is not None: - result['AlgorithmName'] = self.algorithm_name - if self.algorithm_provider is not None: - result['AlgorithmProvider'] = self.algorithm_provider + if self.instance_job_type is not None: + result['InstanceJobType'] = self.instance_job_type + if self.order is not None: + result['Order'] = self.order if self.page_number is not None: result['PageNumber'] = self.page_number if self.page_size is not None: result['PageSize'] = self.page_size - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id + if self.sort_by is not None: + result['SortBy'] = self.sort_by + if self.status is not None: + result['Status'] = self.status return result def from_map(self, m: dict = None): m = m or dict() - if m.get('AlgorithmId') is not None: - self.algorithm_id = m.get('AlgorithmId') - if m.get('AlgorithmName') is not None: - self.algorithm_name = m.get('AlgorithmName') - if m.get('AlgorithmProvider') is not None: - self.algorithm_provider = m.get('AlgorithmProvider') + if m.get('InstanceJobType') is not None: + self.instance_job_type = m.get('InstanceJobType') + if m.get('Order') is not None: + self.order = m.get('Order') if m.get('PageNumber') is not None: self.page_number = m.get('PageNumber') if m.get('PageSize') is not None: self.page_size = m.get('PageSize') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') + if m.get('SortBy') is not None: + self.sort_by = m.get('SortBy') + if m.get('Status') is not None: + self.status = m.get('Status') return self -class ListAlgorithmsResponseBodyAlgorithms(TeaModel): +class ListInstanceJobsResponseBodyInstanceJobs(TeaModel): def __init__( self, - algorithm_description: str = None, - algorithm_id: str = None, - algorithm_name: str = None, - algorithm_provider: str = None, - display_name: str = None, + creator: str = None, gmt_create_time: str = None, - gmt_modified_time: str = None, - user_id: str = None, + instance_id: str = None, + instance_job_id: str = None, + instance_job_type: str = None, + reason_code: str = None, + reason_message: str = None, + status: str = None, workspace_id: str = None, ): - self.algorithm_description = algorithm_description - self.algorithm_id = algorithm_id - self.algorithm_name = algorithm_name - self.algorithm_provider = algorithm_provider - self.display_name = display_name + self.creator = creator self.gmt_create_time = gmt_create_time - self.gmt_modified_time = gmt_modified_time - self.user_id = user_id + self.instance_id = instance_id + self.instance_job_id = instance_job_id + self.instance_job_type = instance_job_type + self.reason_code = reason_code + self.reason_message = reason_message + self.status = status self.workspace_id = workspace_id def validate(self): @@ -11367,65 +16797,63 @@ def to_map(self): return _map result = dict() - if self.algorithm_description is not None: - result['AlgorithmDescription'] = self.algorithm_description - if self.algorithm_id is not None: - result['AlgorithmId'] = self.algorithm_id - if self.algorithm_name is not None: - result['AlgorithmName'] = self.algorithm_name - if self.algorithm_provider is not None: - result['AlgorithmProvider'] = self.algorithm_provider - if self.display_name is not None: - result['DisplayName'] = self.display_name + if self.creator is not None: + result['Creator'] = self.creator if self.gmt_create_time is not None: result['GmtCreateTime'] = self.gmt_create_time - if self.gmt_modified_time is not None: - result['GmtModifiedTime'] = self.gmt_modified_time - if self.user_id is not None: - result['UserId'] = self.user_id + if self.instance_id is not None: + result['InstanceId'] = self.instance_id + if self.instance_job_id is not None: + result['InstanceJobId'] = self.instance_job_id + if self.instance_job_type is not None: + result['InstanceJobType'] = self.instance_job_type + if self.reason_code is not None: + result['ReasonCode'] = self.reason_code + if self.reason_message is not None: + result['ReasonMessage'] = self.reason_message + if self.status is not None: + result['Status'] = self.status if self.workspace_id is not None: result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('AlgorithmDescription') is not None: - self.algorithm_description = m.get('AlgorithmDescription') - if m.get('AlgorithmId') is not None: - self.algorithm_id = m.get('AlgorithmId') - if m.get('AlgorithmName') is not None: - self.algorithm_name = m.get('AlgorithmName') - if m.get('AlgorithmProvider') is not None: - self.algorithm_provider = m.get('AlgorithmProvider') - if m.get('DisplayName') is not None: - self.display_name = m.get('DisplayName') + if m.get('Creator') is not None: + self.creator = m.get('Creator') if m.get('GmtCreateTime') is not None: self.gmt_create_time = m.get('GmtCreateTime') - if m.get('GmtModifiedTime') is not None: - self.gmt_modified_time = m.get('GmtModifiedTime') - if m.get('UserId') is not None: - self.user_id = m.get('UserId') + if m.get('InstanceId') is not None: + self.instance_id = m.get('InstanceId') + if m.get('InstanceJobId') is not None: + self.instance_job_id = m.get('InstanceJobId') + if m.get('InstanceJobType') is not None: + self.instance_job_type = m.get('InstanceJobType') + if m.get('ReasonCode') is not None: + self.reason_code = m.get('ReasonCode') + if m.get('ReasonMessage') is not None: + self.reason_message = m.get('ReasonMessage') + if m.get('Status') is not None: + self.status = m.get('Status') if m.get('WorkspaceId') is not None: self.workspace_id = m.get('WorkspaceId') return self -class ListAlgorithmsResponseBody(TeaModel): +class ListInstanceJobsResponseBody(TeaModel): def __init__( self, - algorithms: List[ListAlgorithmsResponseBodyAlgorithms] = None, + instance_jobs: ListInstanceJobsResponseBodyInstanceJobs = None, request_id: str = None, total_count: int = None, ): - self.algorithms = algorithms + self.instance_jobs = instance_jobs self.request_id = request_id self.total_count = total_count def validate(self): - if self.algorithms: - for k in self.algorithms: - if k: - k.validate() + if self.instance_jobs: + self.instance_jobs.validate() def to_map(self): _map = super().to_map() @@ -11433,10 +16861,8 @@ def to_map(self): return _map result = dict() - result['Algorithms'] = [] - if self.algorithms is not None: - for k in self.algorithms: - result['Algorithms'].append(k.to_map() if k else None) + if self.instance_jobs is not None: + result['InstanceJobs'] = self.instance_jobs.to_map() if self.request_id is not None: result['RequestId'] = self.request_id if self.total_count is not None: @@ -11445,11 +16871,9 @@ def to_map(self): def from_map(self, m: dict = None): m = m or dict() - self.algorithms = [] - if m.get('Algorithms') is not None: - for k in m.get('Algorithms'): - temp_model = ListAlgorithmsResponseBodyAlgorithms() - self.algorithms.append(temp_model.from_map(k)) + if m.get('InstanceJobs') is not None: + temp_model = ListInstanceJobsResponseBodyInstanceJobs() + self.instance_jobs = temp_model.from_map(m['InstanceJobs']) if m.get('RequestId') is not None: self.request_id = m.get('RequestId') if m.get('TotalCount') is not None: @@ -11457,21 +16881,18 @@ def from_map(self, m: dict = None): return self -class ListAlgorithmsResponse(TeaModel): +class ListInstanceJobsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListAlgorithmsResponseBody = None, + body: ListInstanceJobsResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -11496,29 +16917,27 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListAlgorithmsResponseBody() + temp_model = ListInstanceJobsResponseBody() self.body = temp_model.from_map(m['body']) return self -class ListComponentVersionSnapshotsRequest(TeaModel): +class ListLLMProjectsRequest(TeaModel): def __init__( self, - component_id: str = None, order: str = None, page_number: int = None, page_size: int = None, - snapshot_id: str = None, + project_name: str = None, sort_by: str = None, - version: str = None, + workspace_id: str = None, ): - self.component_id = component_id self.order = order self.page_number = page_number self.page_size = page_size - self.snapshot_id = snapshot_id + self.project_name = project_name self.sort_by = sort_by - self.version = version + self.workspace_id = workspace_id def validate(self): pass @@ -11529,64 +16948,139 @@ def to_map(self): return _map result = dict() - if self.component_id is not None: - result['ComponentId'] = self.component_id if self.order is not None: result['Order'] = self.order if self.page_number is not None: result['PageNumber'] = self.page_number if self.page_size is not None: result['PageSize'] = self.page_size - if self.snapshot_id is not None: - result['SnapshotId'] = self.snapshot_id + if self.project_name is not None: + result['ProjectName'] = self.project_name if self.sort_by is not None: result['SortBy'] = self.sort_by - if self.version is not None: - result['Version'] = self.version + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ComponentId') is not None: - self.component_id = m.get('ComponentId') if m.get('Order') is not None: self.order = m.get('Order') if m.get('PageNumber') is not None: self.page_number = m.get('PageNumber') if m.get('PageSize') is not None: self.page_size = m.get('PageSize') - if m.get('SnapshotId') is not None: - self.snapshot_id = m.get('SnapshotId') + if m.get('ProjectName') is not None: + self.project_name = m.get('ProjectName') if m.get('SortBy') is not None: self.sort_by = m.get('SortBy') - if m.get('Version') is not None: - self.version = m.get('Version') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class ListComponentVersionSnapshotsResponseBodySnapshots(TeaModel): +class ListLLMProjectsResponseBodyProjectsLabels(TeaModel): def __init__( self, - component_id: str = None, - description: str = None, - is_current_version: bool = None, - snapshot_id: str = None, - tenant_id: str = None, + key: str = None, + value: str = None, + ): + self.key = key + self.value = value + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.key is not None: + result['Key'] = self.key + if self.value is not None: + result['Value'] = self.value + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Key') is not None: + self.key = m.get('Key') + if m.get('Value') is not None: + self.value = m.get('Value') + return self + + +class ListLLMProjectsResponseBodyProjectsRuntime(TeaModel): + def __init__( + self, + runtime_id: str = None, + runtime_type: str = None, + ): + self.runtime_id = runtime_id + self.runtime_type = runtime_type + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.runtime_id is not None: + result['RuntimeId'] = self.runtime_id + if self.runtime_type is not None: + result['RuntimeType'] = self.runtime_type + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('RuntimeId') is not None: + self.runtime_id = m.get('RuntimeId') + if m.get('RuntimeType') is not None: + self.runtime_type = m.get('RuntimeType') + return self + + +class ListLLMProjectsResponseBodyProjects(TeaModel): + def __init__( + self, + gmt_create_time: str = None, + gmt_modified_time: str = None, + labels: List[ListLLMProjectsResponseBodyProjectsLabels] = None, + owner_id: str = None, + project_description: str = None, + project_id: str = None, + project_name: str = None, + project_type: str = None, + root_path: str = None, + runtime: ListLLMProjectsResponseBodyProjectsRuntime = None, user_id: str = None, - version: str = None, workspace_id: str = None, ): - self.component_id = component_id - self.description = description - self.is_current_version = is_current_version - self.snapshot_id = snapshot_id - self.tenant_id = tenant_id + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time + self.labels = labels + self.owner_id = owner_id + self.project_description = project_description + self.project_id = project_id + self.project_name = project_name + self.project_type = project_type + self.root_path = root_path + self.runtime = runtime self.user_id = user_id - self.version = version self.workspace_id = workspace_id def validate(self): - pass + if self.labels: + for k in self.labels: + if k: + k.validate() + if self.runtime: + self.runtime.validate() def to_map(self): _map = super().to_map() @@ -11594,59 +17088,79 @@ def to_map(self): return _map result = dict() - if self.component_id is not None: - result['ComponentId'] = self.component_id - if self.description is not None: - result['Description'] = self.description - if self.is_current_version is not None: - result['IsCurrentVersion'] = self.is_current_version - if self.snapshot_id is not None: - result['SnapshotId'] = self.snapshot_id - if self.tenant_id is not None: - result['TenantId'] = self.tenant_id + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + if self.owner_id is not None: + result['OwnerId'] = self.owner_id + if self.project_description is not None: + result['ProjectDescription'] = self.project_description + if self.project_id is not None: + result['ProjectId'] = self.project_id + if self.project_name is not None: + result['ProjectName'] = self.project_name + if self.project_type is not None: + result['ProjectType'] = self.project_type + if self.root_path is not None: + result['RootPath'] = self.root_path + if self.runtime is not None: + result['Runtime'] = self.runtime.to_map() if self.user_id is not None: result['UserId'] = self.user_id - if self.version is not None: - result['Version'] = self.version if self.workspace_id is not None: result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ComponentId') is not None: - self.component_id = m.get('ComponentId') - if m.get('Description') is not None: - self.description = m.get('Description') - if m.get('IsCurrentVersion') is not None: - self.is_current_version = m.get('IsCurrentVersion') - if m.get('SnapshotId') is not None: - self.snapshot_id = m.get('SnapshotId') - if m.get('TenantId') is not None: - self.tenant_id = m.get('TenantId') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = ListLLMProjectsResponseBodyProjectsLabels() + self.labels.append(temp_model.from_map(k)) + if m.get('OwnerId') is not None: + self.owner_id = m.get('OwnerId') + if m.get('ProjectDescription') is not None: + self.project_description = m.get('ProjectDescription') + if m.get('ProjectId') is not None: + self.project_id = m.get('ProjectId') + if m.get('ProjectName') is not None: + self.project_name = m.get('ProjectName') + if m.get('ProjectType') is not None: + self.project_type = m.get('ProjectType') + if m.get('RootPath') is not None: + self.root_path = m.get('RootPath') + if m.get('Runtime') is not None: + temp_model = ListLLMProjectsResponseBodyProjectsRuntime() + self.runtime = temp_model.from_map(m['Runtime']) if m.get('UserId') is not None: self.user_id = m.get('UserId') - if m.get('Version') is not None: - self.version = m.get('Version') if m.get('WorkspaceId') is not None: self.workspace_id = m.get('WorkspaceId') return self -class ListComponentVersionSnapshotsResponseBody(TeaModel): +class ListLLMProjectsResponseBody(TeaModel): def __init__( self, + projects: List[ListLLMProjectsResponseBodyProjects] = None, request_id: str = None, - snapshots: List[ListComponentVersionSnapshotsResponseBodySnapshots] = None, - total_count: int = None, ): + self.projects = projects self.request_id = request_id - self.snapshots = snapshots - self.total_count = total_count def validate(self): - if self.snapshots: - for k in self.snapshots: + if self.projects: + for k in self.projects: if k: k.validate() @@ -11656,45 +17170,38 @@ def to_map(self): return _map result = dict() + result['Projects'] = [] + if self.projects is not None: + for k in self.projects: + result['Projects'].append(k.to_map() if k else None) if self.request_id is not None: result['RequestId'] = self.request_id - result['Snapshots'] = [] - if self.snapshots is not None: - for k in self.snapshots: - result['Snapshots'].append(k.to_map() if k else None) - if self.total_count is not None: - result['TotalCount'] = self.total_count return result def from_map(self, m: dict = None): m = m or dict() + self.projects = [] + if m.get('Projects') is not None: + for k in m.get('Projects'): + temp_model = ListLLMProjectsResponseBodyProjects() + self.projects.append(temp_model.from_map(k)) if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - self.snapshots = [] - if m.get('Snapshots') is not None: - for k in m.get('Snapshots'): - temp_model = ListComponentVersionSnapshotsResponseBodySnapshots() - self.snapshots.append(temp_model.from_map(k)) - if m.get('TotalCount') is not None: - self.total_count = m.get('TotalCount') return self -class ListComponentVersionSnapshotsResponse(TeaModel): +class ListLLMProjectsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListComponentVersionSnapshotsResponseBody = None, + body: ListLLMProjectsResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -11719,27 +17226,23 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListComponentVersionSnapshotsResponseBody() + temp_model = ListLLMProjectsResponseBody() self.body = temp_model.from_map(m['body']) return self -class ListComponentVersionsRequest(TeaModel): +class ListLLMSnapshotsRequest(TeaModel): def __init__( self, - labels: Dict[str, str] = None, order: str = None, page_number: int = None, page_size: int = None, sort_by: str = None, - version: str = None, ): - self.labels = labels self.order = order self.page_number = page_number self.page_size = page_size self.sort_by = sort_by - self.version = version def validate(self): pass @@ -11750,8 +17253,6 @@ def to_map(self): return _map result = dict() - if self.labels is not None: - result['Labels'] = self.labels if self.order is not None: result['Order'] = self.order if self.page_number is not None: @@ -11760,14 +17261,10 @@ def to_map(self): result['PageSize'] = self.page_size if self.sort_by is not None: result['SortBy'] = self.sort_by - if self.version is not None: - result['Version'] = self.version return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Labels') is not None: - self.labels = m.get('Labels') if m.get('Order') is not None: self.order = m.get('Order') if m.get('PageNumber') is not None: @@ -11776,27 +17273,17 @@ def from_map(self, m: dict = None): self.page_size = m.get('PageSize') if m.get('SortBy') is not None: self.sort_by = m.get('SortBy') - if m.get('Version') is not None: - self.version = m.get('Version') return self -class ListComponentVersionsShrinkRequest(TeaModel): +class ListLLMSnapshotsResponseBodySnapshotsContentStorage(TeaModel): def __init__( self, - labels_shrink: str = None, - order: str = None, - page_number: int = None, - page_size: int = None, - sort_by: str = None, - version: str = None, + location: str = None, + type: str = None, ): - self.labels_shrink = labels_shrink - self.order = order - self.page_number = page_number - self.page_size = page_size - self.sort_by = sort_by - self.version = version + self.location = location + self.type = type def validate(self): pass @@ -11807,69 +17294,43 @@ def to_map(self): return _map result = dict() - if self.labels_shrink is not None: - result['Labels'] = self.labels_shrink - if self.order is not None: - result['Order'] = self.order - if self.page_number is not None: - result['PageNumber'] = self.page_number - if self.page_size is not None: - result['PageSize'] = self.page_size - if self.sort_by is not None: - result['SortBy'] = self.sort_by - if self.version is not None: - result['Version'] = self.version + if self.location is not None: + result['Location'] = self.location + if self.type is not None: + result['Type'] = self.type return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Labels') is not None: - self.labels_shrink = m.get('Labels') - if m.get('Order') is not None: - self.order = m.get('Order') - if m.get('PageNumber') is not None: - self.page_number = m.get('PageNumber') - if m.get('PageSize') is not None: - self.page_size = m.get('PageSize') - if m.get('SortBy') is not None: - self.sort_by = m.get('SortBy') - if m.get('Version') is not None: - self.version = m.get('Version') + if m.get('Location') is not None: + self.location = m.get('Location') + if m.get('Type') is not None: + self.type = m.get('Type') return self -class ListComponentVersionsResponseBodyComponentVersions(TeaModel): +class ListLLMSnapshotsResponseBodySnapshots(TeaModel): def __init__( self, - component_id: str = None, + content_storage: ListLLMSnapshotsResponseBodySnapshotsContentStorage = None, gmt_create_time: str = None, gmt_modified_time: str = None, - labels: List[Label] = None, - name: str = None, - provider: str = None, - status: str = None, - tenant_id: str = None, + owner_id: str = None, + project_id: str = None, + snapshot_id: str = None, user_id: str = None, - version: str = None, - workspace_id: str = None, ): - self.component_id = component_id + self.content_storage = content_storage self.gmt_create_time = gmt_create_time self.gmt_modified_time = gmt_modified_time - self.labels = labels - self.name = name - self.provider = provider - self.status = status - self.tenant_id = tenant_id + self.owner_id = owner_id + self.project_id = project_id + self.snapshot_id = snapshot_id self.user_id = user_id - self.version = version - self.workspace_id = workspace_id def validate(self): - if self.labels: - for k in self.labels: - if k: - k.validate() + if self.content_storage: + self.content_storage.validate() def to_map(self): _map = super().to_map() @@ -11877,76 +17338,56 @@ def to_map(self): return _map result = dict() - if self.component_id is not None: - result['ComponentId'] = self.component_id + if self.content_storage is not None: + result['ContentStorage'] = self.content_storage.to_map() if self.gmt_create_time is not None: result['GmtCreateTime'] = self.gmt_create_time if self.gmt_modified_time is not None: result['GmtModifiedTime'] = self.gmt_modified_time - result['Labels'] = [] - if self.labels is not None: - for k in self.labels: - result['Labels'].append(k.to_map() if k else None) - if self.name is not None: - result['Name'] = self.name - if self.provider is not None: - result['Provider'] = self.provider - if self.status is not None: - result['Status'] = self.status - if self.tenant_id is not None: - result['TenantId'] = self.tenant_id + if self.owner_id is not None: + result['OwnerId'] = self.owner_id + if self.project_id is not None: + result['ProjectId'] = self.project_id + if self.snapshot_id is not None: + result['SnapshotId'] = self.snapshot_id if self.user_id is not None: result['UserId'] = self.user_id - if self.version is not None: - result['Version'] = self.version - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ComponentId') is not None: - self.component_id = m.get('ComponentId') + if m.get('ContentStorage') is not None: + temp_model = ListLLMSnapshotsResponseBodySnapshotsContentStorage() + self.content_storage = temp_model.from_map(m['ContentStorage']) if m.get('GmtCreateTime') is not None: self.gmt_create_time = m.get('GmtCreateTime') if m.get('GmtModifiedTime') is not None: self.gmt_modified_time = m.get('GmtModifiedTime') - self.labels = [] - if m.get('Labels') is not None: - for k in m.get('Labels'): - temp_model = Label() - self.labels.append(temp_model.from_map(k)) - if m.get('Name') is not None: - self.name = m.get('Name') - if m.get('Provider') is not None: - self.provider = m.get('Provider') - if m.get('Status') is not None: - self.status = m.get('Status') - if m.get('TenantId') is not None: - self.tenant_id = m.get('TenantId') + if m.get('OwnerId') is not None: + self.owner_id = m.get('OwnerId') + if m.get('ProjectId') is not None: + self.project_id = m.get('ProjectId') + if m.get('SnapshotId') is not None: + self.snapshot_id = m.get('SnapshotId') if m.get('UserId') is not None: self.user_id = m.get('UserId') - if m.get('Version') is not None: - self.version = m.get('Version') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') return self -class ListComponentVersionsResponseBody(TeaModel): +class ListLLMSnapshotsResponseBody(TeaModel): def __init__( self, - component_versions: List[ListComponentVersionsResponseBodyComponentVersions] = None, request_id: str = None, + snapshots: List[ListLLMSnapshotsResponseBodySnapshots] = None, total_count: int = None, ): - self.component_versions = component_versions self.request_id = request_id + self.snapshots = snapshots self.total_count = total_count def validate(self): - if self.component_versions: - for k in self.component_versions: + if self.snapshots: + for k in self.snapshots: if k: k.validate() @@ -11956,45 +17397,42 @@ def to_map(self): return _map result = dict() - result['ComponentVersions'] = [] - if self.component_versions is not None: - for k in self.component_versions: - result['ComponentVersions'].append(k.to_map() if k else None) if self.request_id is not None: result['RequestId'] = self.request_id + result['Snapshots'] = [] + if self.snapshots is not None: + for k in self.snapshots: + result['Snapshots'].append(k.to_map() if k else None) if self.total_count is not None: result['TotalCount'] = self.total_count return result def from_map(self, m: dict = None): m = m or dict() - self.component_versions = [] - if m.get('ComponentVersions') is not None: - for k in m.get('ComponentVersions'): - temp_model = ListComponentVersionsResponseBodyComponentVersions() - self.component_versions.append(temp_model.from_map(k)) if m.get('RequestId') is not None: self.request_id = m.get('RequestId') + self.snapshots = [] + if m.get('Snapshots') is not None: + for k in m.get('Snapshots'): + temp_model = ListLLMSnapshotsResponseBodySnapshots() + self.snapshots.append(temp_model.from_map(k)) if m.get('TotalCount') is not None: self.total_count = m.get('TotalCount') return self -class ListComponentVersionsResponse(TeaModel): +class ListLLMSnapshotsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListComponentVersionsResponseBody = None, + body: ListLLMSnapshotsResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -12019,119 +17457,52 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListComponentVersionsResponseBody() + temp_model = ListLLMSnapshotsResponseBody() self.body = temp_model.from_map(m['body']) return self -class ListComponentsRequest(TeaModel): +class ListNodePodsRequest(TeaModel): def __init__( self, - component_id: str = None, - component_ids: str = None, - labels: Dict[str, Any] = None, - name: str = None, - order: str = None, - page_number: int = None, - page_size: int = None, - provider: str = None, - sort_by: str = None, - workspace_id: str = None, - ): - self.component_id = component_id - self.component_ids = component_ids - self.labels = labels - self.name = name - self.order = order - self.page_number = page_number - self.page_size = page_size - self.provider = provider - self.sort_by = sort_by - self.workspace_id = workspace_id - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.component_id is not None: - result['ComponentId'] = self.component_id - if self.component_ids is not None: - result['ComponentIds'] = self.component_ids - if self.labels is not None: - result['Labels'] = self.labels - if self.name is not None: - result['Name'] = self.name - if self.order is not None: - result['Order'] = self.order - if self.page_number is not None: - result['PageNumber'] = self.page_number - if self.page_size is not None: - result['PageSize'] = self.page_size - if self.provider is not None: - result['Provider'] = self.provider - if self.sort_by is not None: - result['SortBy'] = self.sort_by - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('ComponentId') is not None: - self.component_id = m.get('ComponentId') - if m.get('ComponentIds') is not None: - self.component_ids = m.get('ComponentIds') - if m.get('Labels') is not None: - self.labels = m.get('Labels') - if m.get('Name') is not None: - self.name = m.get('Name') - if m.get('Order') is not None: - self.order = m.get('Order') - if m.get('PageNumber') is not None: - self.page_number = m.get('PageNumber') - if m.get('PageSize') is not None: - self.page_size = m.get('PageSize') - if m.get('Provider') is not None: - self.provider = m.get('Provider') - if m.get('SortBy') is not None: - self.sort_by = m.get('SortBy') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') + resource_group_id: str = None, + ): + self.resource_group_id = resource_group_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.resource_group_id is not None: + result['ResourceGroupId'] = self.resource_group_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('ResourceGroupId') is not None: + self.resource_group_id = m.get('ResourceGroupId') return self -class ListComponentsShrinkRequest(TeaModel): +class ListNodePodsResponseBody(TeaModel): def __init__( self, - component_id: str = None, - component_ids: str = None, - labels_shrink: str = None, - name: str = None, - order: str = None, - page_number: int = None, - page_size: int = None, - provider: str = None, - sort_by: str = None, - workspace_id: str = None, + node_pod_infos: List[NodePodInfo] = None, + request_id: str = None, ): - self.component_id = component_id - self.component_ids = component_ids - self.labels_shrink = labels_shrink - self.name = name - self.order = order - self.page_number = page_number - self.page_size = page_size - self.provider = provider - self.sort_by = sort_by - self.workspace_id = workspace_id + self.node_pod_infos = node_pod_infos + self.request_id = request_id def validate(self): - pass + if self.node_pod_infos: + for k in self.node_pod_infos: + if k: + k.validate() def to_map(self): _map = super().to_map() @@ -12139,68 +17510,40 @@ def to_map(self): return _map result = dict() - if self.component_id is not None: - result['ComponentId'] = self.component_id - if self.component_ids is not None: - result['ComponentIds'] = self.component_ids - if self.labels_shrink is not None: - result['Labels'] = self.labels_shrink - if self.name is not None: - result['Name'] = self.name - if self.order is not None: - result['Order'] = self.order - if self.page_number is not None: - result['PageNumber'] = self.page_number - if self.page_size is not None: - result['PageSize'] = self.page_size - if self.provider is not None: - result['Provider'] = self.provider - if self.sort_by is not None: - result['SortBy'] = self.sort_by - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id + result['NodePodInfos'] = [] + if self.node_pod_infos is not None: + for k in self.node_pod_infos: + result['NodePodInfos'].append(k.to_map() if k else None) + if self.request_id is not None: + result['RequestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ComponentId') is not None: - self.component_id = m.get('ComponentId') - if m.get('ComponentIds') is not None: - self.component_ids = m.get('ComponentIds') - if m.get('Labels') is not None: - self.labels_shrink = m.get('Labels') - if m.get('Name') is not None: - self.name = m.get('Name') - if m.get('Order') is not None: - self.order = m.get('Order') - if m.get('PageNumber') is not None: - self.page_number = m.get('PageNumber') - if m.get('PageSize') is not None: - self.page_size = m.get('PageSize') - if m.get('Provider') is not None: - self.provider = m.get('Provider') - if m.get('SortBy') is not None: - self.sort_by = m.get('SortBy') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') + self.node_pod_infos = [] + if m.get('NodePodInfos') is not None: + for k in m.get('NodePodInfos'): + temp_model = NodePodInfo() + self.node_pod_infos.append(temp_model.from_map(k)) + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') return self -class ListComponentsResponseBodyComponentsVersions(TeaModel): +class ListNodePodsResponse(TeaModel): def __init__( self, - gmt_create_time: str = None, - snapshot_id: str = None, - status: str = None, - version: str = None, + headers: Dict[str, str] = None, + status_code: int = None, + body: ListNodePodsResponseBody = None, ): - self.gmt_create_time = gmt_create_time - self.snapshot_id = snapshot_id - self.status = status - self.version = version + self.headers = headers + self.status_code = status_code + self.body = body def validate(self): - pass + if self.body: + self.body.validate() def to_map(self): _map = super().to_map() @@ -12208,67 +17551,43 @@ def to_map(self): return _map result = dict() - if self.gmt_create_time is not None: - result['GmtCreateTime'] = self.gmt_create_time - if self.snapshot_id is not None: - result['SnapshotId'] = self.snapshot_id - if self.status is not None: - result['Status'] = self.status - if self.version is not None: - result['Version'] = self.version + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() return result def from_map(self, m: dict = None): m = m or dict() - if m.get('GmtCreateTime') is not None: - self.gmt_create_time = m.get('GmtCreateTime') - if m.get('SnapshotId') is not None: - self.snapshot_id = m.get('SnapshotId') - if m.get('Status') is not None: - self.status = m.get('Status') - if m.get('Version') is not None: - self.version = m.get('Version') + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = ListNodePodsResponseBody() + self.body = temp_model.from_map(m['body']) return self -class ListComponentsResponseBodyComponents(TeaModel): +class ListNodeTypesRequest(TeaModel): def __init__( self, - component_id: str = None, - description: str = None, - display_name: str = None, - gmt_create_time: str = None, - gmt_modified_time: str = None, - labels: List[Label] = None, - name: str = None, - provider: str = None, - tenant_id: str = None, - user_id: str = None, - versions: List[ListComponentsResponseBodyComponentsVersions] = None, - workspace_id: str = None, + accelerator_type: str = None, + gputype: str = None, + node_types: str = None, + quota_id: str = None, + resource_group_ids: str = None, ): - self.component_id = component_id - self.description = description - self.display_name = display_name - self.gmt_create_time = gmt_create_time - self.gmt_modified_time = gmt_modified_time - self.labels = labels - self.name = name - self.provider = provider - self.tenant_id = tenant_id - self.user_id = user_id - self.versions = versions - self.workspace_id = workspace_id + self.accelerator_type = accelerator_type + self.gputype = gputype + self.node_types = node_types + self.quota_id = quota_id + self.resource_group_ids = resource_group_ids def validate(self): - if self.labels: - for k in self.labels: - if k: - k.validate() - if self.versions: - for k in self.versions: - if k: - k.validate() + pass def to_map(self): _map = super().to_map() @@ -12276,85 +17595,51 @@ def to_map(self): return _map result = dict() - if self.component_id is not None: - result['ComponentId'] = self.component_id - if self.description is not None: - result['Description'] = self.description - if self.display_name is not None: - result['DisplayName'] = self.display_name - if self.gmt_create_time is not None: - result['GmtCreateTime'] = self.gmt_create_time - if self.gmt_modified_time is not None: - result['GmtModifiedTime'] = self.gmt_modified_time - result['Labels'] = [] - if self.labels is not None: - for k in self.labels: - result['Labels'].append(k.to_map() if k else None) - if self.name is not None: - result['Name'] = self.name - if self.provider is not None: - result['Provider'] = self.provider - if self.tenant_id is not None: - result['TenantId'] = self.tenant_id - if self.user_id is not None: - result['UserId'] = self.user_id - result['Versions'] = [] - if self.versions is not None: - for k in self.versions: - result['Versions'].append(k.to_map() if k else None) - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id + if self.accelerator_type is not None: + result['AcceleratorType'] = self.accelerator_type + if self.gputype is not None: + result['GPUType'] = self.gputype + if self.node_types is not None: + result['NodeTypes'] = self.node_types + if self.quota_id is not None: + result['QuotaId'] = self.quota_id + if self.resource_group_ids is not None: + result['ResourceGroupIds'] = self.resource_group_ids return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ComponentId') is not None: - self.component_id = m.get('ComponentId') - if m.get('Description') is not None: - self.description = m.get('Description') - if m.get('DisplayName') is not None: - self.display_name = m.get('DisplayName') - if m.get('GmtCreateTime') is not None: - self.gmt_create_time = m.get('GmtCreateTime') - if m.get('GmtModifiedTime') is not None: - self.gmt_modified_time = m.get('GmtModifiedTime') - self.labels = [] - if m.get('Labels') is not None: - for k in m.get('Labels'): - temp_model = Label() - self.labels.append(temp_model.from_map(k)) - if m.get('Name') is not None: - self.name = m.get('Name') - if m.get('Provider') is not None: - self.provider = m.get('Provider') - if m.get('TenantId') is not None: - self.tenant_id = m.get('TenantId') - if m.get('UserId') is not None: - self.user_id = m.get('UserId') - self.versions = [] - if m.get('Versions') is not None: - for k in m.get('Versions'): - temp_model = ListComponentsResponseBodyComponentsVersions() - self.versions.append(temp_model.from_map(k)) - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') + if m.get('AcceleratorType') is not None: + self.accelerator_type = m.get('AcceleratorType') + if m.get('GPUType') is not None: + self.gputype = m.get('GPUType') + if m.get('NodeTypes') is not None: + self.node_types = m.get('NodeTypes') + if m.get('QuotaId') is not None: + self.quota_id = m.get('QuotaId') + if m.get('ResourceGroupIds') is not None: + self.resource_group_ids = m.get('ResourceGroupIds') return self -class ListComponentsResponseBody(TeaModel): +class ListNodeTypesResponseBody(TeaModel): def __init__( self, - components: List[ListComponentsResponseBodyComponents] = None, + node_types: List[NodeType] = None, request_id: str = None, - total_count: int = None, + statistics: List[NodeTypeStatistic] = None, ): - self.components = components + self.node_types = node_types self.request_id = request_id - self.total_count = total_count + self.statistics = statistics def validate(self): - if self.components: - for k in self.components: + if self.node_types: + for k in self.node_types: + if k: + k.validate() + if self.statistics: + for k in self.statistics: if k: k.validate() @@ -12364,45 +17649,47 @@ def to_map(self): return _map result = dict() - result['Components'] = [] - if self.components is not None: - for k in self.components: - result['Components'].append(k.to_map() if k else None) + result['NodeTypes'] = [] + if self.node_types is not None: + for k in self.node_types: + result['NodeTypes'].append(k.to_map() if k else None) if self.request_id is not None: result['RequestId'] = self.request_id - if self.total_count is not None: - result['TotalCount'] = self.total_count + result['Statistics'] = [] + if self.statistics is not None: + for k in self.statistics: + result['Statistics'].append(k.to_map() if k else None) return result def from_map(self, m: dict = None): m = m or dict() - self.components = [] - if m.get('Components') is not None: - for k in m.get('Components'): - temp_model = ListComponentsResponseBodyComponents() - self.components.append(temp_model.from_map(k)) + self.node_types = [] + if m.get('NodeTypes') is not None: + for k in m.get('NodeTypes'): + temp_model = NodeType() + self.node_types.append(temp_model.from_map(k)) if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('TotalCount') is not None: - self.total_count = m.get('TotalCount') + self.statistics = [] + if m.get('Statistics') is not None: + for k in m.get('Statistics'): + temp_model = NodeTypeStatistic() + self.statistics.append(temp_model.from_map(k)) return self -class ListComponentsResponse(TeaModel): +class ListNodeTypesResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListComponentsResponseBody = None, + body: ListNodeTypesResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -12427,27 +17714,39 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListComponentsResponseBody() + temp_model = ListNodeTypesResponseBody() self.body = temp_model.from_map(m['body']) return self -class ListInstanceJobsRequest(TeaModel): +class ListNodesRequest(TeaModel): def __init__( self, - instance_job_type: str = None, + accelerator_type: str = None, + gputype: str = None, + node_statuses: str = None, + node_types: str = None, order: str = None, + order_statuses: str = None, page_number: int = None, page_size: int = None, + quota_id: str = None, + resource_group_ids: str = None, sort_by: str = None, - status: str = None, + verbose: bool = None, ): - self.instance_job_type = instance_job_type + self.accelerator_type = accelerator_type + self.gputype = gputype + self.node_statuses = node_statuses + self.node_types = node_types self.order = order + self.order_statuses = order_statuses self.page_number = page_number self.page_size = page_size + self.quota_id = quota_id + self.resource_group_ids = resource_group_ids self.sort_by = sort_by - self.status = status + self.verbose = verbose def validate(self): pass @@ -12458,59 +17757,171 @@ def to_map(self): return _map result = dict() - if self.instance_job_type is not None: - result['InstanceJobType'] = self.instance_job_type + if self.accelerator_type is not None: + result['AcceleratorType'] = self.accelerator_type + if self.gputype is not None: + result['GPUType'] = self.gputype + if self.node_statuses is not None: + result['NodeStatuses'] = self.node_statuses + if self.node_types is not None: + result['NodeTypes'] = self.node_types if self.order is not None: result['Order'] = self.order + if self.order_statuses is not None: + result['OrderStatuses'] = self.order_statuses if self.page_number is not None: result['PageNumber'] = self.page_number if self.page_size is not None: result['PageSize'] = self.page_size + if self.quota_id is not None: + result['QuotaId'] = self.quota_id + if self.resource_group_ids is not None: + result['ResourceGroupIds'] = self.resource_group_ids if self.sort_by is not None: result['SortBy'] = self.sort_by - if self.status is not None: - result['Status'] = self.status + if self.verbose is not None: + result['Verbose'] = self.verbose return result def from_map(self, m: dict = None): m = m or dict() - if m.get('InstanceJobType') is not None: - self.instance_job_type = m.get('InstanceJobType') + if m.get('AcceleratorType') is not None: + self.accelerator_type = m.get('AcceleratorType') + if m.get('GPUType') is not None: + self.gputype = m.get('GPUType') + if m.get('NodeStatuses') is not None: + self.node_statuses = m.get('NodeStatuses') + if m.get('NodeTypes') is not None: + self.node_types = m.get('NodeTypes') if m.get('Order') is not None: self.order = m.get('Order') + if m.get('OrderStatuses') is not None: + self.order_statuses = m.get('OrderStatuses') if m.get('PageNumber') is not None: self.page_number = m.get('PageNumber') if m.get('PageSize') is not None: self.page_size = m.get('PageSize') + if m.get('QuotaId') is not None: + self.quota_id = m.get('QuotaId') + if m.get('ResourceGroupIds') is not None: + self.resource_group_ids = m.get('ResourceGroupIds') if m.get('SortBy') is not None: self.sort_by = m.get('SortBy') - if m.get('Status') is not None: - self.status = m.get('Status') + if m.get('Verbose') is not None: + self.verbose = m.get('Verbose') return self -class ListInstanceJobsResponseBodyInstanceJobs(TeaModel): +class ListNodesResponseBody(TeaModel): def __init__( self, - creator: str = None, - gmt_create_time: str = None, - instance_id: str = None, - instance_job_id: str = None, - instance_job_type: str = None, - reason_code: str = None, - reason_message: str = None, + nodes: List[Node] = None, + request_id: str = None, + total_count: int = None, + ): + self.nodes = nodes + self.request_id = request_id + self.total_count = total_count + + def validate(self): + if self.nodes: + for k in self.nodes: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + result['Nodes'] = [] + if self.nodes is not None: + for k in self.nodes: + result['Nodes'].append(k.to_map() if k else None) + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.total_count is not None: + result['TotalCount'] = self.total_count + return result + + def from_map(self, m: dict = None): + m = m or dict() + self.nodes = [] + if m.get('Nodes') is not None: + for k in m.get('Nodes'): + temp_model = Node() + self.nodes.append(temp_model.from_map(k)) + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') + return self + + +class ListNodesResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: ListNodesResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = ListNodesResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class ListOperationsRequest(TeaModel): + def __init__( + self, + object_id: str = None, + object_type: str = None, + operation_id: str = None, + operation_type: str = None, + order: str = None, + page_number: int = None, + page_size: int = None, + sort_by: str = None, status: str = None, - workspace_id: str = None, ): - self.creator = creator - self.gmt_create_time = gmt_create_time - self.instance_id = instance_id - self.instance_job_id = instance_job_id - self.instance_job_type = instance_job_type - self.reason_code = reason_code - self.reason_message = reason_message + self.object_id = object_id + self.object_type = object_type + self.operation_id = operation_id + self.operation_type = operation_type + self.order = order + self.page_number = page_number + self.page_size = page_size + self.sort_by = sort_by self.status = status - self.workspace_id = workspace_id def validate(self): pass @@ -12520,64 +17931,64 @@ def to_map(self): if _map is not None: return _map - result = dict() - if self.creator is not None: - result['Creator'] = self.creator - if self.gmt_create_time is not None: - result['GmtCreateTime'] = self.gmt_create_time - if self.instance_id is not None: - result['InstanceId'] = self.instance_id - if self.instance_job_id is not None: - result['InstanceJobId'] = self.instance_job_id - if self.instance_job_type is not None: - result['InstanceJobType'] = self.instance_job_type - if self.reason_code is not None: - result['ReasonCode'] = self.reason_code - if self.reason_message is not None: - result['ReasonMessage'] = self.reason_message + result = dict() + if self.object_id is not None: + result['ObjectId'] = self.object_id + if self.object_type is not None: + result['ObjectType'] = self.object_type + if self.operation_id is not None: + result['OperationId'] = self.operation_id + if self.operation_type is not None: + result['OperationType'] = self.operation_type + if self.order is not None: + result['Order'] = self.order + if self.page_number is not None: + result['PageNumber'] = self.page_number + if self.page_size is not None: + result['PageSize'] = self.page_size + if self.sort_by is not None: + result['SortBy'] = self.sort_by if self.status is not None: result['Status'] = self.status - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Creator') is not None: - self.creator = m.get('Creator') - if m.get('GmtCreateTime') is not None: - self.gmt_create_time = m.get('GmtCreateTime') - if m.get('InstanceId') is not None: - self.instance_id = m.get('InstanceId') - if m.get('InstanceJobId') is not None: - self.instance_job_id = m.get('InstanceJobId') - if m.get('InstanceJobType') is not None: - self.instance_job_type = m.get('InstanceJobType') - if m.get('ReasonCode') is not None: - self.reason_code = m.get('ReasonCode') - if m.get('ReasonMessage') is not None: - self.reason_message = m.get('ReasonMessage') + if m.get('ObjectId') is not None: + self.object_id = m.get('ObjectId') + if m.get('ObjectType') is not None: + self.object_type = m.get('ObjectType') + if m.get('OperationId') is not None: + self.operation_id = m.get('OperationId') + if m.get('OperationType') is not None: + self.operation_type = m.get('OperationType') + if m.get('Order') is not None: + self.order = m.get('Order') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + if m.get('SortBy') is not None: + self.sort_by = m.get('SortBy') if m.get('Status') is not None: self.status = m.get('Status') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') return self -class ListInstanceJobsResponseBody(TeaModel): +class ListOperationsResponseBody(TeaModel): def __init__( self, - instance_jobs: ListInstanceJobsResponseBodyInstanceJobs = None, + operations: List[ResourceOperation] = None, request_id: str = None, - total_count: int = None, ): - self.instance_jobs = instance_jobs + self.operations = operations self.request_id = request_id - self.total_count = total_count def validate(self): - if self.instance_jobs: - self.instance_jobs.validate() + if self.operations: + for k in self.operations: + if k: + k.validate() def to_map(self): _map = super().to_map() @@ -12585,41 +17996,38 @@ def to_map(self): return _map result = dict() - if self.instance_jobs is not None: - result['InstanceJobs'] = self.instance_jobs.to_map() + result['Operations'] = [] + if self.operations is not None: + for k in self.operations: + result['Operations'].append(k.to_map() if k else None) if self.request_id is not None: result['RequestId'] = self.request_id - if self.total_count is not None: - result['TotalCount'] = self.total_count return result def from_map(self, m: dict = None): m = m or dict() - if m.get('InstanceJobs') is not None: - temp_model = ListInstanceJobsResponseBodyInstanceJobs() - self.instance_jobs = temp_model.from_map(m['InstanceJobs']) + self.operations = [] + if m.get('Operations') is not None: + for k in m.get('Operations'): + temp_model = ResourceOperation() + self.operations.append(temp_model.from_map(k)) if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('TotalCount') is not None: - self.total_count = m.get('TotalCount') return self -class ListInstanceJobsResponse(TeaModel): +class ListOperationsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListInstanceJobsResponseBody = None, + body: ListOperationsResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -12644,80 +18052,27 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListInstanceJobsResponseBody() + temp_model = ListOperationsResponseBody() self.body = temp_model.from_map(m['body']) return self -class ListNodeTypesRequest(TeaModel): - def __init__( - self, - accelerator_type: str = None, - gputype: str = None, - node_types: str = None, - quota_id: str = None, - resource_group_ids: str = None, - ): - self.accelerator_type = accelerator_type - self.gputype = gputype - self.node_types = node_types - self.quota_id = quota_id - self.resource_group_ids = resource_group_ids - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.accelerator_type is not None: - result['AcceleratorType'] = self.accelerator_type - if self.gputype is not None: - result['GPUType'] = self.gputype - if self.node_types is not None: - result['NodeTypes'] = self.node_types - if self.quota_id is not None: - result['QuotaId'] = self.quota_id - if self.resource_group_ids is not None: - result['ResourceGroupIds'] = self.resource_group_ids - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('AcceleratorType') is not None: - self.accelerator_type = m.get('AcceleratorType') - if m.get('GPUType') is not None: - self.gputype = m.get('GPUType') - if m.get('NodeTypes') is not None: - self.node_types = m.get('NodeTypes') - if m.get('QuotaId') is not None: - self.quota_id = m.get('QuotaId') - if m.get('ResourceGroupIds') is not None: - self.resource_group_ids = m.get('ResourceGroupIds') - return self - - -class ListNodeTypesResponseBody(TeaModel): +class ListPermissionsResponseBody(TeaModel): def __init__( self, - node_types: List[NodeType] = None, + features: Features = None, + permissions: List[Permission] = None, request_id: str = None, - statistics: List[NodeTypeStatistic] = None, ): - self.node_types = node_types + self.features = features + self.permissions = permissions self.request_id = request_id - self.statistics = statistics def validate(self): - if self.node_types: - for k in self.node_types: - if k: - k.validate() - if self.statistics: - for k in self.statistics: + if self.features: + self.features.validate() + if self.permissions: + for k in self.permissions: if k: k.validate() @@ -12727,50 +18082,43 @@ def to_map(self): return _map result = dict() - result['NodeTypes'] = [] - if self.node_types is not None: - for k in self.node_types: - result['NodeTypes'].append(k.to_map() if k else None) + if self.features is not None: + result['Features'] = self.features.to_map() + result['Permissions'] = [] + if self.permissions is not None: + for k in self.permissions: + result['Permissions'].append(k.to_map() if k else None) if self.request_id is not None: - result['RequestId'] = self.request_id - result['Statistics'] = [] - if self.statistics is not None: - for k in self.statistics: - result['Statistics'].append(k.to_map() if k else None) + result['requestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() - self.node_types = [] - if m.get('NodeTypes') is not None: - for k in m.get('NodeTypes'): - temp_model = NodeType() - self.node_types.append(temp_model.from_map(k)) - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - self.statistics = [] - if m.get('Statistics') is not None: - for k in m.get('Statistics'): - temp_model = NodeTypeStatistic() - self.statistics.append(temp_model.from_map(k)) + if m.get('Features') is not None: + temp_model = Features() + self.features = temp_model.from_map(m['Features']) + self.permissions = [] + if m.get('Permissions') is not None: + for k in m.get('Permissions'): + temp_model = Permission() + self.permissions.append(temp_model.from_map(k)) + if m.get('requestId') is not None: + self.request_id = m.get('requestId') return self -class ListNodeTypesResponse(TeaModel): +class ListPermissionsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListNodeTypesResponseBody = None, + body: ListPermissionsResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -12795,37 +18143,39 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListNodeTypesResponseBody() + temp_model = ListPermissionsResponseBody() self.body = temp_model.from_map(m['body']) return self -class ListNodesRequest(TeaModel): +class ListQuotasRequest(TeaModel): def __init__( self, - accelerator_type: str = None, - gputype: str = None, - node_statuses: str = None, - node_types: str = None, + labels: str = None, + layout_mode: str = None, order: str = None, - order_statuses: str = None, page_number: int = None, page_size: int = None, - quota_id: str = None, - resource_group_ids: str = None, + parent_quota_id: str = None, + quota_ids: str = None, + quota_name: str = None, + resource_type: str = None, sort_by: str = None, + statuses: str = None, + workspace_ids: str = None, ): - self.accelerator_type = accelerator_type - self.gputype = gputype - self.node_statuses = node_statuses - self.node_types = node_types + self.labels = labels + self.layout_mode = layout_mode self.order = order - self.order_statuses = order_statuses self.page_number = page_number self.page_size = page_size - self.quota_id = quota_id - self.resource_group_ids = resource_group_ids + self.parent_quota_id = parent_quota_id + self.quota_ids = quota_ids + self.quota_name = quota_name + self.resource_type = resource_type self.sort_by = sort_by + self.statuses = statuses + self.workspace_ids = workspace_ids def validate(self): pass @@ -12836,69 +18186,75 @@ def to_map(self): return _map result = dict() - if self.accelerator_type is not None: - result['AcceleratorType'] = self.accelerator_type - if self.gputype is not None: - result['GPUType'] = self.gputype - if self.node_statuses is not None: - result['NodeStatuses'] = self.node_statuses - if self.node_types is not None: - result['NodeTypes'] = self.node_types + if self.labels is not None: + result['Labels'] = self.labels + if self.layout_mode is not None: + result['LayoutMode'] = self.layout_mode if self.order is not None: result['Order'] = self.order - if self.order_statuses is not None: - result['OrderStatuses'] = self.order_statuses if self.page_number is not None: result['PageNumber'] = self.page_number if self.page_size is not None: result['PageSize'] = self.page_size - if self.quota_id is not None: - result['QuotaId'] = self.quota_id - if self.resource_group_ids is not None: - result['ResourceGroupIds'] = self.resource_group_ids + if self.parent_quota_id is not None: + result['ParentQuotaId'] = self.parent_quota_id + if self.quota_ids is not None: + result['QuotaIds'] = self.quota_ids + if self.quota_name is not None: + result['QuotaName'] = self.quota_name + if self.resource_type is not None: + result['ResourceType'] = self.resource_type if self.sort_by is not None: result['SortBy'] = self.sort_by + if self.statuses is not None: + result['Statuses'] = self.statuses + if self.workspace_ids is not None: + result['WorkspaceIds'] = self.workspace_ids return result def from_map(self, m: dict = None): m = m or dict() - if m.get('AcceleratorType') is not None: - self.accelerator_type = m.get('AcceleratorType') - if m.get('GPUType') is not None: - self.gputype = m.get('GPUType') - if m.get('NodeStatuses') is not None: - self.node_statuses = m.get('NodeStatuses') - if m.get('NodeTypes') is not None: - self.node_types = m.get('NodeTypes') + if m.get('Labels') is not None: + self.labels = m.get('Labels') + if m.get('LayoutMode') is not None: + self.layout_mode = m.get('LayoutMode') if m.get('Order') is not None: self.order = m.get('Order') - if m.get('OrderStatuses') is not None: - self.order_statuses = m.get('OrderStatuses') if m.get('PageNumber') is not None: self.page_number = m.get('PageNumber') if m.get('PageSize') is not None: self.page_size = m.get('PageSize') - if m.get('QuotaId') is not None: - self.quota_id = m.get('QuotaId') - if m.get('ResourceGroupIds') is not None: - self.resource_group_ids = m.get('ResourceGroupIds') + if m.get('ParentQuotaId') is not None: + self.parent_quota_id = m.get('ParentQuotaId') + if m.get('QuotaIds') is not None: + self.quota_ids = m.get('QuotaIds') + if m.get('QuotaName') is not None: + self.quota_name = m.get('QuotaName') + if m.get('ResourceType') is not None: + self.resource_type = m.get('ResourceType') if m.get('SortBy') is not None: self.sort_by = m.get('SortBy') + if m.get('Statuses') is not None: + self.statuses = m.get('Statuses') + if m.get('WorkspaceIds') is not None: + self.workspace_ids = m.get('WorkspaceIds') return self -class ListNodesResponseBody(TeaModel): +class ListQuotasResponseBody(TeaModel): def __init__( self, - nodes: List[Node] = None, + quotas: List[Quota] = None, request_id: str = None, + total_count: int = None, ): - self.nodes = nodes + self.quotas = quotas self.request_id = request_id + self.total_count = total_count def validate(self): - if self.nodes: - for k in self.nodes: + if self.quotas: + for k in self.quotas: if k: k.validate() @@ -12908,41 +18264,42 @@ def to_map(self): return _map result = dict() - result['Nodes'] = [] - if self.nodes is not None: - for k in self.nodes: - result['Nodes'].append(k.to_map() if k else None) + result['Quotas'] = [] + if self.quotas is not None: + for k in self.quotas: + result['Quotas'].append(k.to_map() if k else None) if self.request_id is not None: result['RequestId'] = self.request_id + if self.total_count is not None: + result['TotalCount'] = self.total_count return result def from_map(self, m: dict = None): m = m or dict() - self.nodes = [] - if m.get('Nodes') is not None: - for k in m.get('Nodes'): - temp_model = Node() - self.nodes.append(temp_model.from_map(k)) + self.quotas = [] + if m.get('Quotas') is not None: + for k in m.get('Quotas'): + temp_model = Quota() + self.quotas.append(temp_model.from_map(k)) if m.get('RequestId') is not None: self.request_id = m.get('RequestId') + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') return self -class ListNodesResponse(TeaModel): +class ListQuotasResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListNodesResponseBody = None, + body: ListQuotasResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -12967,31 +18324,35 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListNodesResponseBody() + temp_model = ListQuotasResponseBody() self.body = temp_model.from_map(m['body']) return self -class ListOperationsRequest(TeaModel): +class ListResourceGroupMachineGroupsRequest(TeaModel): def __init__( self, - object_id: str = None, - object_type: str = None, - operation_id: str = None, - operation_type: str = None, + creator_id: str = None, + ecs_spec: str = None, + name: str = None, order: str = None, page_number: int = None, page_size: int = None, + payment_duration: str = None, + payment_duration_unit: str = None, + payment_type: str = None, sort_by: str = None, status: str = None, ): - self.object_id = object_id - self.object_type = object_type - self.operation_id = operation_id - self.operation_type = operation_type + self.creator_id = creator_id + self.ecs_spec = ecs_spec + self.name = name self.order = order self.page_number = page_number self.page_size = page_size + self.payment_duration = payment_duration + self.payment_duration_unit = payment_duration_unit + self.payment_type = payment_type self.sort_by = sort_by self.status = status @@ -13004,20 +18365,24 @@ def to_map(self): return _map result = dict() - if self.object_id is not None: - result['ObjectId'] = self.object_id - if self.object_type is not None: - result['ObjectType'] = self.object_type - if self.operation_id is not None: - result['OperationId'] = self.operation_id - if self.operation_type is not None: - result['OperationType'] = self.operation_type + if self.creator_id is not None: + result['CreatorID'] = self.creator_id + if self.ecs_spec is not None: + result['EcsSpec'] = self.ecs_spec + if self.name is not None: + result['Name'] = self.name if self.order is not None: result['Order'] = self.order if self.page_number is not None: result['PageNumber'] = self.page_number if self.page_size is not None: result['PageSize'] = self.page_size + if self.payment_duration is not None: + result['PaymentDuration'] = self.payment_duration + if self.payment_duration_unit is not None: + result['PaymentDurationUnit'] = self.payment_duration_unit + if self.payment_type is not None: + result['PaymentType'] = self.payment_type if self.sort_by is not None: result['SortBy'] = self.sort_by if self.status is not None: @@ -13026,20 +18391,24 @@ def to_map(self): def from_map(self, m: dict = None): m = m or dict() - if m.get('ObjectId') is not None: - self.object_id = m.get('ObjectId') - if m.get('ObjectType') is not None: - self.object_type = m.get('ObjectType') - if m.get('OperationId') is not None: - self.operation_id = m.get('OperationId') - if m.get('OperationType') is not None: - self.operation_type = m.get('OperationType') + if m.get('CreatorID') is not None: + self.creator_id = m.get('CreatorID') + if m.get('EcsSpec') is not None: + self.ecs_spec = m.get('EcsSpec') + if m.get('Name') is not None: + self.name = m.get('Name') if m.get('Order') is not None: self.order = m.get('Order') if m.get('PageNumber') is not None: self.page_number = m.get('PageNumber') if m.get('PageSize') is not None: self.page_size = m.get('PageSize') + if m.get('PaymentDuration') is not None: + self.payment_duration = m.get('PaymentDuration') + if m.get('PaymentDurationUnit') is not None: + self.payment_duration_unit = m.get('PaymentDurationUnit') + if m.get('PaymentType') is not None: + self.payment_type = m.get('PaymentType') if m.get('SortBy') is not None: self.sort_by = m.get('SortBy') if m.get('Status') is not None: @@ -13047,18 +18416,20 @@ def from_map(self, m: dict = None): return self -class ListOperationsResponseBody(TeaModel): +class ListResourceGroupMachineGroupsResponseBody(TeaModel): def __init__( self, - operations: List[ResourceOperation] = None, + machine_groups: List[MachineGroup] = None, request_id: str = None, + total_count: str = None, ): - self.operations = operations + self.machine_groups = machine_groups self.request_id = request_id + self.total_count = total_count def validate(self): - if self.operations: - for k in self.operations: + if self.machine_groups: + for k in self.machine_groups: if k: k.validate() @@ -13068,41 +18439,42 @@ def to_map(self): return _map result = dict() - result['Operations'] = [] - if self.operations is not None: - for k in self.operations: - result['Operations'].append(k.to_map() if k else None) + result['MachineGroups'] = [] + if self.machine_groups is not None: + for k in self.machine_groups: + result['MachineGroups'].append(k.to_map() if k else None) if self.request_id is not None: result['RequestId'] = self.request_id + if self.total_count is not None: + result['TotalCount'] = self.total_count return result def from_map(self, m: dict = None): m = m or dict() - self.operations = [] - if m.get('Operations') is not None: - for k in m.get('Operations'): - temp_model = ResourceOperation() - self.operations.append(temp_model.from_map(k)) + self.machine_groups = [] + if m.get('MachineGroups') is not None: + for k in m.get('MachineGroups'): + temp_model = MachineGroup() + self.machine_groups.append(temp_model.from_map(k)) if m.get('RequestId') is not None: self.request_id = m.get('RequestId') + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') return self -class ListOperationsResponse(TeaModel): +class ListResourceGroupMachineGroupsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListOperationsResponseBody = None, + body: ListResourceGroupMachineGroupsResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -13127,27 +18499,100 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListOperationsResponseBody() + temp_model = ListResourceGroupMachineGroupsResponseBody() self.body = temp_model.from_map(m['body']) return self -class ListPermissionsResponseBody(TeaModel): +class ListResourceGroupsRequest(TeaModel): + def __init__( + self, + computing_resource_provider: str = None, + name: str = None, + order: str = None, + page_number: int = None, + page_size: int = None, + resource_type: str = None, + show_all: bool = None, + sort_by: str = None, + status: str = None, + ): + self.computing_resource_provider = computing_resource_provider + self.name = name + self.order = order + self.page_number = page_number + self.page_size = page_size + self.resource_type = resource_type + self.show_all = show_all + self.sort_by = sort_by + self.status = status + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.computing_resource_provider is not None: + result['ComputingResourceProvider'] = self.computing_resource_provider + if self.name is not None: + result['Name'] = self.name + if self.order is not None: + result['Order'] = self.order + if self.page_number is not None: + result['PageNumber'] = self.page_number + if self.page_size is not None: + result['PageSize'] = self.page_size + if self.resource_type is not None: + result['ResourceType'] = self.resource_type + if self.show_all is not None: + result['ShowAll'] = self.show_all + if self.sort_by is not None: + result['SortBy'] = self.sort_by + if self.status is not None: + result['Status'] = self.status + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('ComputingResourceProvider') is not None: + self.computing_resource_provider = m.get('ComputingResourceProvider') + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('Order') is not None: + self.order = m.get('Order') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + if m.get('ResourceType') is not None: + self.resource_type = m.get('ResourceType') + if m.get('ShowAll') is not None: + self.show_all = m.get('ShowAll') + if m.get('SortBy') is not None: + self.sort_by = m.get('SortBy') + if m.get('Status') is not None: + self.status = m.get('Status') + return self + + +class ListResourceGroupsResponseBody(TeaModel): def __init__( self, - features: Features = None, - permissions: List[Permission] = None, request_id: str = None, + resource_groups: List[ResourceGroup] = None, + total_count: int = None, ): - self.features = features - self.permissions = permissions self.request_id = request_id + self.resource_groups = resource_groups + self.total_count = total_count def validate(self): - if self.features: - self.features.validate() - if self.permissions: - for k in self.permissions: + if self.resource_groups: + for k in self.resource_groups: if k: k.validate() @@ -13157,46 +18602,42 @@ def to_map(self): return _map result = dict() - if self.features is not None: - result['Features'] = self.features.to_map() - result['Permissions'] = [] - if self.permissions is not None: - for k in self.permissions: - result['Permissions'].append(k.to_map() if k else None) if self.request_id is not None: - result['requestId'] = self.request_id + result['RequestId'] = self.request_id + result['ResourceGroups'] = [] + if self.resource_groups is not None: + for k in self.resource_groups: + result['ResourceGroups'].append(k.to_map() if k else None) + if self.total_count is not None: + result['TotalCount'] = self.total_count return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Features') is not None: - temp_model = Features() - self.features = temp_model.from_map(m['Features']) - self.permissions = [] - if m.get('Permissions') is not None: - for k in m.get('Permissions'): - temp_model = Permission() - self.permissions.append(temp_model.from_map(k)) - if m.get('requestId') is not None: - self.request_id = m.get('requestId') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + self.resource_groups = [] + if m.get('ResourceGroups') is not None: + for k in m.get('ResourceGroups'): + temp_model = ResourceGroup() + self.resource_groups.append(temp_model.from_map(k)) + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') return self -class ListPermissionsResponse(TeaModel): +class ListResourceGroupsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListPermissionsResponseBody = None, + body: ListResourceGroupsResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -13221,35 +18662,17 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListPermissionsResponseBody() + temp_model = ListResourceGroupsResponseBody() self.body = temp_model.from_map(m['body']) return self -class ListQuotasRequest(TeaModel): +class ListSpotsStockPreviewRequest(TeaModel): def __init__( self, - order: str = None, - page_number: int = None, - page_size: int = None, - parent_quota_id: str = None, - quota_ids: str = None, - quota_name: str = None, - resource_type: str = None, - sort_by: str = None, - statuses: str = None, - workspace_ids: str = None, + instance_types: str = None, ): - self.order = order - self.page_number = page_number - self.page_size = page_size - self.parent_quota_id = parent_quota_id - self.quota_ids = quota_ids - self.quota_name = quota_name - self.resource_type = resource_type - self.sort_by = sort_by - self.statuses = statuses - self.workspace_ids = workspace_ids + self.instance_types = instance_types def validate(self): pass @@ -13260,65 +18683,29 @@ def to_map(self): return _map result = dict() - if self.order is not None: - result['Order'] = self.order - if self.page_number is not None: - result['PageNumber'] = self.page_number - if self.page_size is not None: - result['PageSize'] = self.page_size - if self.parent_quota_id is not None: - result['ParentQuotaId'] = self.parent_quota_id - if self.quota_ids is not None: - result['QuotaIds'] = self.quota_ids - if self.quota_name is not None: - result['QuotaName'] = self.quota_name - if self.resource_type is not None: - result['ResourceType'] = self.resource_type - if self.sort_by is not None: - result['SortBy'] = self.sort_by - if self.statuses is not None: - result['Statuses'] = self.statuses - if self.workspace_ids is not None: - result['WorkspaceIds'] = self.workspace_ids + if self.instance_types is not None: + result['InstanceTypes'] = self.instance_types return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Order') is not None: - self.order = m.get('Order') - if m.get('PageNumber') is not None: - self.page_number = m.get('PageNumber') - if m.get('PageSize') is not None: - self.page_size = m.get('PageSize') - if m.get('ParentQuotaId') is not None: - self.parent_quota_id = m.get('ParentQuotaId') - if m.get('QuotaIds') is not None: - self.quota_ids = m.get('QuotaIds') - if m.get('QuotaName') is not None: - self.quota_name = m.get('QuotaName') - if m.get('ResourceType') is not None: - self.resource_type = m.get('ResourceType') - if m.get('SortBy') is not None: - self.sort_by = m.get('SortBy') - if m.get('Statuses') is not None: - self.statuses = m.get('Statuses') - if m.get('WorkspaceIds') is not None: - self.workspace_ids = m.get('WorkspaceIds') + if m.get('InstanceTypes') is not None: + self.instance_types = m.get('InstanceTypes') return self -class ListQuotasResponseBody(TeaModel): +class ListSpotsStockPreviewResponseBody(TeaModel): def __init__( self, - quotas: List[Quota] = None, request_id: str = None, + spots_stock_preview: List[SpotStockPreview] = None, ): - self.quotas = quotas self.request_id = request_id + self.spots_stock_preview = spots_stock_preview def validate(self): - if self.quotas: - for k in self.quotas: + if self.spots_stock_preview: + for k in self.spots_stock_preview: if k: k.validate() @@ -13328,41 +18715,38 @@ def to_map(self): return _map result = dict() - result['Quotas'] = [] - if self.quotas is not None: - for k in self.quotas: - result['Quotas'].append(k.to_map() if k else None) if self.request_id is not None: result['RequestId'] = self.request_id + result['SpotsStockPreview'] = [] + if self.spots_stock_preview is not None: + for k in self.spots_stock_preview: + result['SpotsStockPreview'].append(k.to_map() if k else None) return result def from_map(self, m: dict = None): m = m or dict() - self.quotas = [] - if m.get('Quotas') is not None: - for k in m.get('Quotas'): - temp_model = Quota() - self.quotas.append(temp_model.from_map(k)) if m.get('RequestId') is not None: self.request_id = m.get('RequestId') + self.spots_stock_preview = [] + if m.get('SpotsStockPreview') is not None: + for k in m.get('SpotsStockPreview'): + temp_model = SpotStockPreview() + self.spots_stock_preview.append(temp_model.from_map(k)) return self -class ListQuotasResponse(TeaModel): +class ListSpotsStockPreviewResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListQuotasResponseBody = None, + body: ListSpotsStockPreviewResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -13387,37 +18771,19 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListQuotasResponseBody() + temp_model = ListSpotsStockPreviewResponseBody() self.body = temp_model.from_map(m['body']) return self -class ListResourceGroupMachineGroupsRequest(TeaModel): +class ListTagResourcesRequestTag(TeaModel): def __init__( self, - creator_id: str = None, - ecs_spec: str = None, - name: str = None, - order: str = None, - page_number: int = None, - page_size: int = None, - payment_duration: str = None, - payment_duration_unit: str = None, - payment_type: str = None, - sort_by: str = None, - status: str = None, + key: str = None, + value: str = None, ): - self.creator_id = creator_id - self.ecs_spec = ecs_spec - self.name = name - self.order = order - self.page_number = page_number - self.page_size = page_size - self.payment_duration = payment_duration - self.payment_duration_unit = payment_duration_unit - self.payment_type = payment_type - self.sort_by = sort_by - self.status = status + self.key = key + self.value = value def validate(self): pass @@ -13428,71 +18794,39 @@ def to_map(self): return _map result = dict() - if self.creator_id is not None: - result['CreatorID'] = self.creator_id - if self.ecs_spec is not None: - result['EcsSpec'] = self.ecs_spec - if self.name is not None: - result['Name'] = self.name - if self.order is not None: - result['Order'] = self.order - if self.page_number is not None: - result['PageNumber'] = self.page_number - if self.page_size is not None: - result['PageSize'] = self.page_size - if self.payment_duration is not None: - result['PaymentDuration'] = self.payment_duration - if self.payment_duration_unit is not None: - result['PaymentDurationUnit'] = self.payment_duration_unit - if self.payment_type is not None: - result['PaymentType'] = self.payment_type - if self.sort_by is not None: - result['SortBy'] = self.sort_by - if self.status is not None: - result['Status'] = self.status + if self.key is not None: + result['Key'] = self.key + if self.value is not None: + result['Value'] = self.value return result def from_map(self, m: dict = None): m = m or dict() - if m.get('CreatorID') is not None: - self.creator_id = m.get('CreatorID') - if m.get('EcsSpec') is not None: - self.ecs_spec = m.get('EcsSpec') - if m.get('Name') is not None: - self.name = m.get('Name') - if m.get('Order') is not None: - self.order = m.get('Order') - if m.get('PageNumber') is not None: - self.page_number = m.get('PageNumber') - if m.get('PageSize') is not None: - self.page_size = m.get('PageSize') - if m.get('PaymentDuration') is not None: - self.payment_duration = m.get('PaymentDuration') - if m.get('PaymentDurationUnit') is not None: - self.payment_duration_unit = m.get('PaymentDurationUnit') - if m.get('PaymentType') is not None: - self.payment_type = m.get('PaymentType') - if m.get('SortBy') is not None: - self.sort_by = m.get('SortBy') - if m.get('Status') is not None: - self.status = m.get('Status') + if m.get('Key') is not None: + self.key = m.get('Key') + if m.get('Value') is not None: + self.value = m.get('Value') return self -class ListResourceGroupMachineGroupsResponseBody(TeaModel): +class ListTagResourcesRequest(TeaModel): def __init__( self, - machine_groups: List[MachineGroup] = None, - request_id: str = None, - total_count: str = None, + next_token: str = None, + region_id: str = None, + resource_id: List[str] = None, + resource_type: str = None, + tag: List[ListTagResourcesRequestTag] = None, ): - self.machine_groups = machine_groups - self.request_id = request_id - self.total_count = total_count + self.next_token = next_token + self.region_id = region_id + self.resource_id = resource_id + self.resource_type = resource_type + self.tag = tag def validate(self): - if self.machine_groups: - for k in self.machine_groups: + if self.tag: + for k in self.tag: if k: k.validate() @@ -13502,47 +18836,55 @@ def to_map(self): return _map result = dict() - result['MachineGroups'] = [] - if self.machine_groups is not None: - for k in self.machine_groups: - result['MachineGroups'].append(k.to_map() if k else None) - if self.request_id is not None: - result['RequestId'] = self.request_id - if self.total_count is not None: - result['TotalCount'] = self.total_count + if self.next_token is not None: + result['NextToken'] = self.next_token + if self.region_id is not None: + result['RegionId'] = self.region_id + if self.resource_id is not None: + result['ResourceId'] = self.resource_id + if self.resource_type is not None: + result['ResourceType'] = self.resource_type + result['Tag'] = [] + if self.tag is not None: + for k in self.tag: + result['Tag'].append(k.to_map() if k else None) return result def from_map(self, m: dict = None): m = m or dict() - self.machine_groups = [] - if m.get('MachineGroups') is not None: - for k in m.get('MachineGroups'): - temp_model = MachineGroup() - self.machine_groups.append(temp_model.from_map(k)) - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - if m.get('TotalCount') is not None: - self.total_count = m.get('TotalCount') + if m.get('NextToken') is not None: + self.next_token = m.get('NextToken') + if m.get('RegionId') is not None: + self.region_id = m.get('RegionId') + if m.get('ResourceId') is not None: + self.resource_id = m.get('ResourceId') + if m.get('ResourceType') is not None: + self.resource_type = m.get('ResourceType') + self.tag = [] + if m.get('Tag') is not None: + for k in m.get('Tag'): + temp_model = ListTagResourcesRequestTag() + self.tag.append(temp_model.from_map(k)) return self -class ListResourceGroupMachineGroupsResponse(TeaModel): +class ListTagResourcesShrinkRequest(TeaModel): def __init__( self, - headers: Dict[str, str] = None, - status_code: int = None, - body: ListResourceGroupMachineGroupsResponseBody = None, - ): - self.headers = headers - self.status_code = status_code - self.body = body + next_token: str = None, + region_id: str = None, + resource_id_shrink: str = None, + resource_type: str = None, + tag_shrink: str = None, + ): + self.next_token = next_token + self.region_id = region_id + self.resource_id_shrink = resource_id_shrink + self.resource_type = resource_type + self.tag_shrink = tag_shrink def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') - if self.body: - self.body.validate() + pass def to_map(self): _map = super().to_map() @@ -13550,48 +18892,45 @@ def to_map(self): return _map result = dict() - if self.headers is not None: - result['headers'] = self.headers - if self.status_code is not None: - result['statusCode'] = self.status_code - if self.body is not None: - result['body'] = self.body.to_map() + if self.next_token is not None: + result['NextToken'] = self.next_token + if self.region_id is not None: + result['RegionId'] = self.region_id + if self.resource_id_shrink is not None: + result['ResourceId'] = self.resource_id_shrink + if self.resource_type is not None: + result['ResourceType'] = self.resource_type + if self.tag_shrink is not None: + result['Tag'] = self.tag_shrink return result def from_map(self, m: dict = None): m = m or dict() - if m.get('headers') is not None: - self.headers = m.get('headers') - if m.get('statusCode') is not None: - self.status_code = m.get('statusCode') - if m.get('body') is not None: - temp_model = ListResourceGroupMachineGroupsResponseBody() - self.body = temp_model.from_map(m['body']) + if m.get('NextToken') is not None: + self.next_token = m.get('NextToken') + if m.get('RegionId') is not None: + self.region_id = m.get('RegionId') + if m.get('ResourceId') is not None: + self.resource_id_shrink = m.get('ResourceId') + if m.get('ResourceType') is not None: + self.resource_type = m.get('ResourceType') + if m.get('Tag') is not None: + self.tag_shrink = m.get('Tag') return self -class ListResourceGroupsRequest(TeaModel): +class ListTagResourcesResponseBodyTagResources(TeaModel): def __init__( self, - computing_resource_provider: str = None, - name: str = None, - order: str = None, - page_number: int = None, - page_size: int = None, + resource_id: str = None, resource_type: str = None, - show_all: bool = None, - sort_by: str = None, - status: str = None, + tag_key: str = None, + tag_value: str = None, ): - self.computing_resource_provider = computing_resource_provider - self.name = name - self.order = order - self.page_number = page_number - self.page_size = page_size + self.resource_id = resource_id self.resource_type = resource_type - self.show_all = show_all - self.sort_by = sort_by - self.status = status + self.tag_key = tag_key + self.tag_value = tag_value def validate(self): pass @@ -13602,63 +18941,43 @@ def to_map(self): return _map result = dict() - if self.computing_resource_provider is not None: - result['ComputingResourceProvider'] = self.computing_resource_provider - if self.name is not None: - result['Name'] = self.name - if self.order is not None: - result['Order'] = self.order - if self.page_number is not None: - result['PageNumber'] = self.page_number - if self.page_size is not None: - result['PageSize'] = self.page_size + if self.resource_id is not None: + result['ResourceId'] = self.resource_id if self.resource_type is not None: result['ResourceType'] = self.resource_type - if self.show_all is not None: - result['ShowAll'] = self.show_all - if self.sort_by is not None: - result['SortBy'] = self.sort_by - if self.status is not None: - result['Status'] = self.status + if self.tag_key is not None: + result['TagKey'] = self.tag_key + if self.tag_value is not None: + result['TagValue'] = self.tag_value return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ComputingResourceProvider') is not None: - self.computing_resource_provider = m.get('ComputingResourceProvider') - if m.get('Name') is not None: - self.name = m.get('Name') - if m.get('Order') is not None: - self.order = m.get('Order') - if m.get('PageNumber') is not None: - self.page_number = m.get('PageNumber') - if m.get('PageSize') is not None: - self.page_size = m.get('PageSize') + if m.get('ResourceId') is not None: + self.resource_id = m.get('ResourceId') if m.get('ResourceType') is not None: self.resource_type = m.get('ResourceType') - if m.get('ShowAll') is not None: - self.show_all = m.get('ShowAll') - if m.get('SortBy') is not None: - self.sort_by = m.get('SortBy') - if m.get('Status') is not None: - self.status = m.get('Status') + if m.get('TagKey') is not None: + self.tag_key = m.get('TagKey') + if m.get('TagValue') is not None: + self.tag_value = m.get('TagValue') return self -class ListResourceGroupsResponseBody(TeaModel): +class ListTagResourcesResponseBody(TeaModel): def __init__( self, + next_token: str = None, request_id: str = None, - resource_groups: List[ResourceGroup] = None, - total_count: int = None, + tag_resources: List[ListTagResourcesResponseBodyTagResources] = None, ): + self.next_token = next_token self.request_id = request_id - self.resource_groups = resource_groups - self.total_count = total_count + self.tag_resources = tag_resources def validate(self): - if self.resource_groups: - for k in self.resource_groups: + if self.tag_resources: + for k in self.tag_resources: if k: k.validate() @@ -13668,45 +18987,42 @@ def to_map(self): return _map result = dict() + if self.next_token is not None: + result['NextToken'] = self.next_token if self.request_id is not None: result['RequestId'] = self.request_id - result['ResourceGroups'] = [] - if self.resource_groups is not None: - for k in self.resource_groups: - result['ResourceGroups'].append(k.to_map() if k else None) - if self.total_count is not None: - result['TotalCount'] = self.total_count + result['TagResources'] = [] + if self.tag_resources is not None: + for k in self.tag_resources: + result['TagResources'].append(k.to_map() if k else None) return result def from_map(self, m: dict = None): m = m or dict() + if m.get('NextToken') is not None: + self.next_token = m.get('NextToken') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - self.resource_groups = [] - if m.get('ResourceGroups') is not None: - for k in m.get('ResourceGroups'): - temp_model = ResourceGroup() - self.resource_groups.append(temp_model.from_map(k)) - if m.get('TotalCount') is not None: - self.total_count = m.get('TotalCount') + self.tag_resources = [] + if m.get('TagResources') is not None: + for k in m.get('TagResources'): + temp_model = ListTagResourcesResponseBodyTagResources() + self.tag_resources.append(temp_model.from_map(k)) return self -class ListResourceGroupsResponse(TeaModel): +class ListTagResourcesResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListResourceGroupsResponseBody = None, + body: ListTagResourcesResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -13731,7 +19047,7 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListResourceGroupsResponseBody() + temp_model = ListTagResourcesResponseBody() self.body = temp_model.from_map(m['body']) return self @@ -13838,9 +19154,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -13972,9 +19285,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -14194,9 +19504,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -14230,6 +19537,7 @@ class ListTrainingJobLogsRequest(TeaModel): def __init__( self, end_time: str = None, + instance_id: str = None, page_number: int = None, page_size: int = None, start_time: str = None, @@ -14237,6 +19545,7 @@ def __init__( worker_id: str = None, ): self.end_time = end_time + self.instance_id = instance_id self.page_number = page_number self.page_size = page_size self.start_time = start_time @@ -14254,6 +19563,8 @@ def to_map(self): result = dict() if self.end_time is not None: result['EndTime'] = self.end_time + if self.instance_id is not None: + result['InstanceId'] = self.instance_id if self.page_number is not None: result['PageNumber'] = self.page_number if self.page_size is not None: @@ -14270,6 +19581,8 @@ def from_map(self, m: dict = None): m = m or dict() if m.get('EndTime') is not None: self.end_time = m.get('EndTime') + if m.get('InstanceId') is not None: + self.instance_id = m.get('InstanceId') if m.get('PageNumber') is not None: self.page_number = m.get('PageNumber') if m.get('PageSize') is not None: @@ -14334,9 +19647,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -14433,11 +19743,157 @@ class ListTrainingJobMetricsResponseBodyMetrics(TeaModel): def __init__( self, name: str = None, - timestamp: str = None, - value: float = None, + timestamp: str = None, + value: float = None, + ): + self.name = name + self.timestamp = timestamp + self.value = value + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.name is not None: + result['Name'] = self.name + if self.timestamp is not None: + result['Timestamp'] = self.timestamp + if self.value is not None: + result['Value'] = self.value + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('Timestamp') is not None: + self.timestamp = m.get('Timestamp') + if m.get('Value') is not None: + self.value = m.get('Value') + return self + + +class ListTrainingJobMetricsResponseBody(TeaModel): + def __init__( + self, + metrics: List[ListTrainingJobMetricsResponseBodyMetrics] = None, + request_id: str = None, + ): + self.metrics = metrics + self.request_id = request_id + + def validate(self): + if self.metrics: + for k in self.metrics: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + result['Metrics'] = [] + if self.metrics is not None: + for k in self.metrics: + result['Metrics'].append(k.to_map() if k else None) + if self.request_id is not None: + result['RequestId'] = self.request_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + self.metrics = [] + if m.get('Metrics') is not None: + for k in m.get('Metrics'): + temp_model = ListTrainingJobMetricsResponseBodyMetrics() + self.metrics.append(temp_model.from_map(k)) + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + return self + + +class ListTrainingJobMetricsResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: ListTrainingJobMetricsResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = ListTrainingJobMetricsResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class ListTrainingJobOutputModelsRequest(TeaModel): + def __init__( + self, + token: str = None, + ): + self.token = token + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.token is not None: + result['Token'] = self.token + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Token') is not None: + self.token = m.get('Token') + return self + + +class ListTrainingJobOutputModelsResponseBodyOutputModelsLabels(TeaModel): + def __init__( + self, + name: str = None, + value: str = None, ): self.name = name - self.timestamp = timestamp self.value = value def validate(self): @@ -14451,8 +19907,6 @@ def to_map(self): result = dict() if self.name is not None: result['Name'] = self.name - if self.timestamp is not None: - result['Timestamp'] = self.timestamp if self.value is not None: result['Value'] = self.value return result @@ -14461,25 +19915,37 @@ def from_map(self, m: dict = None): m = m or dict() if m.get('Name') is not None: self.name = m.get('Name') - if m.get('Timestamp') is not None: - self.timestamp = m.get('Timestamp') if m.get('Value') is not None: self.value = m.get('Value') return self -class ListTrainingJobMetricsResponseBody(TeaModel): +class ListTrainingJobOutputModelsResponseBodyOutputModels(TeaModel): def __init__( self, - metrics: List[ListTrainingJobMetricsResponseBodyMetrics] = None, - request_id: str = None, - ): + evaluation_spec: Dict[str, Any] = None, + inference_spec: Dict[str, Any] = None, + labels: List[ListTrainingJobOutputModelsResponseBodyOutputModelsLabels] = None, + metrics: Dict[str, Any] = None, + output_channel_name: str = None, + source_id: str = None, + source_type: str = None, + training_spec: Dict[str, Any] = None, + uri: str = None, + ): + self.evaluation_spec = evaluation_spec + self.inference_spec = inference_spec + self.labels = labels self.metrics = metrics - self.request_id = request_id + self.output_channel_name = output_channel_name + self.source_id = source_id + self.source_type = source_type + self.training_spec = training_spec + self.uri = uri def validate(self): - if self.metrics: - for k in self.metrics: + if self.labels: + for k in self.labels: if k: k.validate() @@ -14489,41 +19955,101 @@ def to_map(self): return _map result = dict() - result['Metrics'] = [] + if self.evaluation_spec is not None: + result['EvaluationSpec'] = self.evaluation_spec + if self.inference_spec is not None: + result['InferenceSpec'] = self.inference_spec + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) if self.metrics is not None: - for k in self.metrics: - result['Metrics'].append(k.to_map() if k else None) - if self.request_id is not None: - result['RequestId'] = self.request_id + result['Metrics'] = self.metrics + if self.output_channel_name is not None: + result['OutputChannelName'] = self.output_channel_name + if self.source_id is not None: + result['SourceId'] = self.source_id + if self.source_type is not None: + result['SourceType'] = self.source_type + if self.training_spec is not None: + result['TrainingSpec'] = self.training_spec + if self.uri is not None: + result['Uri'] = self.uri return result def from_map(self, m: dict = None): m = m or dict() - self.metrics = [] + if m.get('EvaluationSpec') is not None: + self.evaluation_spec = m.get('EvaluationSpec') + if m.get('InferenceSpec') is not None: + self.inference_spec = m.get('InferenceSpec') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = ListTrainingJobOutputModelsResponseBodyOutputModelsLabels() + self.labels.append(temp_model.from_map(k)) if m.get('Metrics') is not None: - for k in m.get('Metrics'): - temp_model = ListTrainingJobMetricsResponseBodyMetrics() - self.metrics.append(temp_model.from_map(k)) - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') + self.metrics = m.get('Metrics') + if m.get('OutputChannelName') is not None: + self.output_channel_name = m.get('OutputChannelName') + if m.get('SourceId') is not None: + self.source_id = m.get('SourceId') + if m.get('SourceType') is not None: + self.source_type = m.get('SourceType') + if m.get('TrainingSpec') is not None: + self.training_spec = m.get('TrainingSpec') + if m.get('Uri') is not None: + self.uri = m.get('Uri') return self -class ListTrainingJobMetricsResponse(TeaModel): +class ListTrainingJobOutputModelsResponseBody(TeaModel): + def __init__( + self, + output_models: List[ListTrainingJobOutputModelsResponseBodyOutputModels] = None, + ): + self.output_models = output_models + + def validate(self): + if self.output_models: + for k in self.output_models: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + result['OutputModels'] = [] + if self.output_models is not None: + for k in self.output_models: + result['OutputModels'].append(k.to_map() if k else None) + return result + + def from_map(self, m: dict = None): + m = m or dict() + self.output_models = [] + if m.get('OutputModels') is not None: + for k in m.get('OutputModels'): + temp_model = ListTrainingJobOutputModelsResponseBodyOutputModels() + self.output_models.append(temp_model.from_map(k)) + return self + + +class ListTrainingJobOutputModelsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListTrainingJobMetricsResponseBody = None, + body: ListTrainingJobOutputModelsResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -14548,7 +20074,7 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListTrainingJobMetricsResponseBody() + temp_model = ListTrainingJobOutputModelsResponseBody() self.body = temp_model.from_map(m['body']) return self @@ -14763,17 +20289,75 @@ def from_map(self, m: dict = None): return self +class ListTrainingJobsResponseBodyTrainingJobsComputeResourceInstanceSpec(TeaModel): + def __init__( + self, + cpu: str = None, + gpu: str = None, + gputype: str = None, + memory: str = None, + shared_memory: str = None, + ): + self.cpu = cpu + self.gpu = gpu + self.gputype = gputype + self.memory = memory + self.shared_memory = shared_memory + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.cpu is not None: + result['CPU'] = self.cpu + if self.gpu is not None: + result['GPU'] = self.gpu + if self.gputype is not None: + result['GPUType'] = self.gputype + if self.memory is not None: + result['Memory'] = self.memory + if self.shared_memory is not None: + result['SharedMemory'] = self.shared_memory + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('CPU') is not None: + self.cpu = m.get('CPU') + if m.get('GPU') is not None: + self.gpu = m.get('GPU') + if m.get('GPUType') is not None: + self.gputype = m.get('GPUType') + if m.get('Memory') is not None: + self.memory = m.get('Memory') + if m.get('SharedMemory') is not None: + self.shared_memory = m.get('SharedMemory') + return self + + class ListTrainingJobsResponseBodyTrainingJobsComputeResource(TeaModel): def __init__( self, ecs_count: int = None, ecs_spec: str = None, + instance_count: int = None, + instance_spec: ListTrainingJobsResponseBodyTrainingJobsComputeResourceInstanceSpec = None, + resource_id: str = None, ): self.ecs_count = ecs_count self.ecs_spec = ecs_spec + self.instance_count = instance_count + self.instance_spec = instance_spec + self.resource_id = resource_id def validate(self): - pass + if self.instance_spec: + self.instance_spec.validate() def to_map(self): _map = super().to_map() @@ -14785,6 +20369,12 @@ def to_map(self): result['EcsCount'] = self.ecs_count if self.ecs_spec is not None: result['EcsSpec'] = self.ecs_spec + if self.instance_count is not None: + result['InstanceCount'] = self.instance_count + if self.instance_spec is not None: + result['InstanceSpec'] = self.instance_spec.to_map() + if self.resource_id is not None: + result['ResourceId'] = self.resource_id return result def from_map(self, m: dict = None): @@ -14793,6 +20383,46 @@ def from_map(self, m: dict = None): self.ecs_count = m.get('EcsCount') if m.get('EcsSpec') is not None: self.ecs_spec = m.get('EcsSpec') + if m.get('InstanceCount') is not None: + self.instance_count = m.get('InstanceCount') + if m.get('InstanceSpec') is not None: + temp_model = ListTrainingJobsResponseBodyTrainingJobsComputeResourceInstanceSpec() + self.instance_spec = temp_model.from_map(m['InstanceSpec']) + if m.get('ResourceId') is not None: + self.resource_id = m.get('ResourceId') + return self + + +class ListTrainingJobsResponseBodyTrainingJobsExperimentConfig(TeaModel): + def __init__( + self, + experiment_id: str = None, + experiment_name: str = None, + ): + self.experiment_id = experiment_id + self.experiment_name = experiment_name + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.experiment_id is not None: + result['ExperimentId'] = self.experiment_id + if self.experiment_name is not None: + result['ExperimentName'] = self.experiment_name + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('ExperimentId') is not None: + self.experiment_id = m.get('ExperimentId') + if m.get('ExperimentName') is not None: + self.experiment_name = m.get('ExperimentName') return self @@ -15021,11 +20651,13 @@ def from_map(self, m: dict = None): class ListTrainingJobsResponseBodyTrainingJobsUserVpc(TeaModel): def __init__( self, + default_route: str = None, extended_cidrs: List[str] = None, security_group_id: str = None, switch_id: str = None, vpc_id: str = None, ): + self.default_route = default_route self.extended_cidrs = extended_cidrs self.security_group_id = security_group_id self.switch_id = switch_id @@ -15040,6 +20672,8 @@ def to_map(self): return _map result = dict() + if self.default_route is not None: + result['DefaultRoute'] = self.default_route if self.extended_cidrs is not None: result['ExtendedCIDRs'] = self.extended_cidrs if self.security_group_id is not None: @@ -15052,6 +20686,8 @@ def to_map(self): def from_map(self, m: dict = None): m = m or dict() + if m.get('DefaultRoute') is not None: + self.default_route = m.get('DefaultRoute') if m.get('ExtendedCIDRs') is not None: self.extended_cidrs = m.get('ExtendedCIDRs') if m.get('SecurityGroupId') is not None: @@ -15070,6 +20706,8 @@ def __init__( algorithm_provider: str = None, algorithm_version: str = None, compute_resource: ListTrainingJobsResponseBodyTrainingJobsComputeResource = None, + environments: Dict[str, str] = None, + experiment_config: ListTrainingJobsResponseBodyTrainingJobsExperimentConfig = None, gmt_create_time: str = None, gmt_modified_time: str = None, hyper_parameters: List[ListTrainingJobsResponseBodyTrainingJobsHyperParameters] = None, @@ -15077,6 +20715,7 @@ def __init__( is_temp_algo: bool = None, labels: List[ListTrainingJobsResponseBodyTrainingJobsLabels] = None, output_channels: List[ListTrainingJobsResponseBodyTrainingJobsOutputChannels] = None, + python_requirements: List[str] = None, reason_code: str = None, reason_message: str = None, role_arn: str = None, @@ -15094,6 +20733,8 @@ def __init__( self.algorithm_provider = algorithm_provider self.algorithm_version = algorithm_version self.compute_resource = compute_resource + self.environments = environments + self.experiment_config = experiment_config self.gmt_create_time = gmt_create_time self.gmt_modified_time = gmt_modified_time self.hyper_parameters = hyper_parameters @@ -15101,6 +20742,7 @@ def __init__( self.is_temp_algo = is_temp_algo self.labels = labels self.output_channels = output_channels + self.python_requirements = python_requirements self.reason_code = reason_code self.reason_message = reason_message self.role_arn = role_arn @@ -15117,6 +20759,8 @@ def __init__( def validate(self): if self.compute_resource: self.compute_resource.validate() + if self.experiment_config: + self.experiment_config.validate() if self.hyper_parameters: for k in self.hyper_parameters: if k: @@ -15156,6 +20800,10 @@ def to_map(self): result['AlgorithmVersion'] = self.algorithm_version if self.compute_resource is not None: result['ComputeResource'] = self.compute_resource.to_map() + if self.environments is not None: + result['Environments'] = self.environments + if self.experiment_config is not None: + result['ExperimentConfig'] = self.experiment_config.to_map() if self.gmt_create_time is not None: result['GmtCreateTime'] = self.gmt_create_time if self.gmt_modified_time is not None: @@ -15178,6 +20826,8 @@ def to_map(self): if self.output_channels is not None: for k in self.output_channels: result['OutputChannels'].append(k.to_map() if k else None) + if self.python_requirements is not None: + result['PythonRequirements'] = self.python_requirements if self.reason_code is not None: result['ReasonCode'] = self.reason_code if self.reason_message is not None: @@ -15217,6 +20867,11 @@ def from_map(self, m: dict = None): if m.get('ComputeResource') is not None: temp_model = ListTrainingJobsResponseBodyTrainingJobsComputeResource() self.compute_resource = temp_model.from_map(m['ComputeResource']) + if m.get('Environments') is not None: + self.environments = m.get('Environments') + if m.get('ExperimentConfig') is not None: + temp_model = ListTrainingJobsResponseBodyTrainingJobsExperimentConfig() + self.experiment_config = temp_model.from_map(m['ExperimentConfig']) if m.get('GmtCreateTime') is not None: self.gmt_create_time = m.get('GmtCreateTime') if m.get('GmtModifiedTime') is not None: @@ -15243,6 +20898,8 @@ def from_map(self, m: dict = None): for k in m.get('OutputChannels'): temp_model = ListTrainingJobsResponseBodyTrainingJobsOutputChannels() self.output_channels.append(temp_model.from_map(k)) + if m.get('PythonRequirements') is not None: + self.python_requirements = m.get('PythonRequirements') if m.get('ReasonCode') is not None: self.reason_code = m.get('ReasonCode') if m.get('ReasonMessage') is not None: @@ -15275,22 +20932,138 @@ def from_map(self, m: dict = None): return self -class ListTrainingJobsResponseBody(TeaModel): +class ListTrainingJobsResponseBody(TeaModel): + def __init__( + self, + request_id: str = None, + total_count: int = None, + training_jobs: List[ListTrainingJobsResponseBodyTrainingJobs] = None, + ): + self.request_id = request_id + self.total_count = total_count + self.training_jobs = training_jobs + + def validate(self): + if self.training_jobs: + for k in self.training_jobs: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.total_count is not None: + result['TotalCount'] = self.total_count + result['TrainingJobs'] = [] + if self.training_jobs is not None: + for k in self.training_jobs: + result['TrainingJobs'].append(k.to_map() if k else None) + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') + self.training_jobs = [] + if m.get('TrainingJobs') is not None: + for k in m.get('TrainingJobs'): + temp_model = ListTrainingJobsResponseBodyTrainingJobs() + self.training_jobs.append(temp_model.from_map(k)) + return self + + +class ListTrainingJobsResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: ListTrainingJobsResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = ListTrainingJobsResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class OperateNodeRequest(TeaModel): + def __init__( + self, + operation: str = None, + resource_group_id: str = None, + ): + self.operation = operation + self.resource_group_id = resource_group_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.operation is not None: + result['Operation'] = self.operation + if self.resource_group_id is not None: + result['ResourceGroupId'] = self.resource_group_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Operation') is not None: + self.operation = m.get('Operation') + if m.get('ResourceGroupId') is not None: + self.resource_group_id = m.get('ResourceGroupId') + return self + + +class OperateNodeResponseBody(TeaModel): def __init__( self, + node_id: str = None, request_id: str = None, - total_count: int = None, - training_jobs: List[ListTrainingJobsResponseBodyTrainingJobs] = None, ): + self.node_id = node_id self.request_id = request_id - self.total_count = total_count - self.training_jobs = training_jobs def validate(self): - if self.training_jobs: - for k in self.training_jobs: - if k: - k.validate() + pass def to_map(self): _map = super().to_map() @@ -15298,45 +21071,33 @@ def to_map(self): return _map result = dict() + if self.node_id is not None: + result['NodeId'] = self.node_id if self.request_id is not None: result['RequestId'] = self.request_id - if self.total_count is not None: - result['TotalCount'] = self.total_count - result['TrainingJobs'] = [] - if self.training_jobs is not None: - for k in self.training_jobs: - result['TrainingJobs'].append(k.to_map() if k else None) return result def from_map(self, m: dict = None): m = m or dict() + if m.get('NodeId') is not None: + self.node_id = m.get('NodeId') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('TotalCount') is not None: - self.total_count = m.get('TotalCount') - self.training_jobs = [] - if m.get('TrainingJobs') is not None: - for k in m.get('TrainingJobs'): - temp_model = ListTrainingJobsResponseBodyTrainingJobs() - self.training_jobs.append(temp_model.from_map(k)) return self -class ListTrainingJobsResponse(TeaModel): +class OperateNodeResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListTrainingJobsResponseBody = None, + body: OperateNodeResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -15361,7 +21122,7 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListTrainingJobsResponseBody() + temp_model = OperateNodeResponseBody() self.body = temp_model.from_map(m['body']) return self @@ -15444,9 +21205,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -15566,9 +21324,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -15601,7 +21356,7 @@ def from_map(self, m: dict = None): class ScaleQuotaRequest(TeaModel): def __init__( self, - min: AllocateStrategySpec = None, + min: ResourceSpec = None, resource_group_ids: List[str] = None, ): self.min = min @@ -15626,7 +21381,7 @@ def to_map(self): def from_map(self, m: dict = None): m = m or dict() if m.get('Min') is not None: - temp_model = AllocateStrategySpec() + temp_model = ResourceSpec() self.min = temp_model.from_map(m['Min']) if m.get('ResourceGroupIds') is not None: self.resource_group_ids = m.get('ResourceGroupIds') @@ -15639,6 +21394,7 @@ def __init__( quota_id: str = None, request_id: str = None, ): + # Quota Id self.quota_id = quota_id self.request_id = request_id @@ -15678,9 +21434,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -15710,7 +21463,7 @@ def from_map(self, m: dict = None): return self -class StopArrearsTrainingJobResponseBody(TeaModel): +class StopTrainingJobResponseBody(TeaModel): def __init__( self, request_id: str = None, @@ -15737,21 +21490,18 @@ def from_map(self, m: dict = None): return self -class StopArrearsTrainingJobResponse(TeaModel): +class StopTrainingJobResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: StopArrearsTrainingJobResponseBody = None, + body: StopTrainingJobResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -15776,12 +21526,98 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = StopArrearsTrainingJobResponseBody() + temp_model = StopTrainingJobResponseBody() self.body = temp_model.from_map(m['body']) return self -class StopTrainingJobResponseBody(TeaModel): +class TagResourcesRequestTag(TeaModel): + def __init__( + self, + key: str = None, + value: str = None, + ): + self.key = key + self.value = value + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.key is not None: + result['Key'] = self.key + if self.value is not None: + result['Value'] = self.value + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Key') is not None: + self.key = m.get('Key') + if m.get('Value') is not None: + self.value = m.get('Value') + return self + + +class TagResourcesRequest(TeaModel): + def __init__( + self, + region_id: str = None, + resource_id: List[str] = None, + resource_type: str = None, + tag: List[TagResourcesRequestTag] = None, + ): + self.region_id = region_id + self.resource_id = resource_id + self.resource_type = resource_type + self.tag = tag + + def validate(self): + if self.tag: + for k in self.tag: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.region_id is not None: + result['RegionId'] = self.region_id + if self.resource_id is not None: + result['ResourceId'] = self.resource_id + if self.resource_type is not None: + result['ResourceType'] = self.resource_type + result['Tag'] = [] + if self.tag is not None: + for k in self.tag: + result['Tag'].append(k.to_map() if k else None) + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('RegionId') is not None: + self.region_id = m.get('RegionId') + if m.get('ResourceId') is not None: + self.resource_id = m.get('ResourceId') + if m.get('ResourceType') is not None: + self.resource_type = m.get('ResourceType') + self.tag = [] + if m.get('Tag') is not None: + for k in m.get('Tag'): + temp_model = TagResourcesRequestTag() + self.tag.append(temp_model.from_map(k)) + return self + + +class TagResourcesResponseBody(TeaModel): def __init__( self, request_id: str = None, @@ -15808,21 +21644,18 @@ def from_map(self, m: dict = None): return self -class StopTrainingJobResponse(TeaModel): +class TagResourcesResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: StopTrainingJobResponseBody = None, + body: TagResourcesResponseBody = None, ): self.headers = headers self.status_code = status_code self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -15847,7 +21680,177 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = StopTrainingJobResponseBody() + temp_model = TagResourcesResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class UntagResourcesRequest(TeaModel): + def __init__( + self, + all: bool = None, + region_id: str = None, + resource_id: List[str] = None, + resource_type: str = None, + tag_key: List[str] = None, + ): + self.all = all + self.region_id = region_id + self.resource_id = resource_id + self.resource_type = resource_type + self.tag_key = tag_key + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.all is not None: + result['All'] = self.all + if self.region_id is not None: + result['RegionId'] = self.region_id + if self.resource_id is not None: + result['ResourceId'] = self.resource_id + if self.resource_type is not None: + result['ResourceType'] = self.resource_type + if self.tag_key is not None: + result['TagKey'] = self.tag_key + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('All') is not None: + self.all = m.get('All') + if m.get('RegionId') is not None: + self.region_id = m.get('RegionId') + if m.get('ResourceId') is not None: + self.resource_id = m.get('ResourceId') + if m.get('ResourceType') is not None: + self.resource_type = m.get('ResourceType') + if m.get('TagKey') is not None: + self.tag_key = m.get('TagKey') + return self + + +class UntagResourcesShrinkRequest(TeaModel): + def __init__( + self, + all: bool = None, + region_id: str = None, + resource_id_shrink: str = None, + resource_type: str = None, + tag_key_shrink: str = None, + ): + self.all = all + self.region_id = region_id + self.resource_id_shrink = resource_id_shrink + self.resource_type = resource_type + self.tag_key_shrink = tag_key_shrink + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.all is not None: + result['All'] = self.all + if self.region_id is not None: + result['RegionId'] = self.region_id + if self.resource_id_shrink is not None: + result['ResourceId'] = self.resource_id_shrink + if self.resource_type is not None: + result['ResourceType'] = self.resource_type + if self.tag_key_shrink is not None: + result['TagKey'] = self.tag_key_shrink + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('All') is not None: + self.all = m.get('All') + if m.get('RegionId') is not None: + self.region_id = m.get('RegionId') + if m.get('ResourceId') is not None: + self.resource_id_shrink = m.get('ResourceId') + if m.get('ResourceType') is not None: + self.resource_type = m.get('ResourceType') + if m.get('TagKey') is not None: + self.tag_key_shrink = m.get('TagKey') + return self + + +class UntagResourcesResponseBody(TeaModel): + def __init__( + self, + request_id: str = None, + ): + self.request_id = request_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.request_id is not None: + result['RequestId'] = self.request_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + return self + + +class UntagResourcesResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: UntagResourcesResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = UntagResourcesResponseBody() self.body = temp_model.from_map(m['body']) return self @@ -15930,9 +21933,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -16063,9 +22063,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -16181,9 +22178,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -16293,9 +22287,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -16370,9 +22361,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -16402,14 +22390,218 @@ def from_map(self, m: dict = None): return self +class UpdateLLMProjectRequestLabels(TeaModel): + def __init__( + self, + key: str = None, + value: str = None, + ): + self.key = key + self.value = value + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.key is not None: + result['Key'] = self.key + if self.value is not None: + result['Value'] = self.value + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Key') is not None: + self.key = m.get('Key') + if m.get('Value') is not None: + self.value = m.get('Value') + return self + + +class UpdateLLMProjectRequestRuntime(TeaModel): + def __init__( + self, + runtime_id: str = None, + runtime_type: str = None, + ): + self.runtime_id = runtime_id + self.runtime_type = runtime_type + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.runtime_id is not None: + result['RuntimeId'] = self.runtime_id + if self.runtime_type is not None: + result['RuntimeType'] = self.runtime_type + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('RuntimeId') is not None: + self.runtime_id = m.get('RuntimeId') + if m.get('RuntimeType') is not None: + self.runtime_type = m.get('RuntimeType') + return self + + +class UpdateLLMProjectRequest(TeaModel): + def __init__( + self, + labels: List[UpdateLLMProjectRequestLabels] = None, + project_description: str = None, + project_name: str = None, + root_path: str = None, + runtime: UpdateLLMProjectRequestRuntime = None, + ): + self.labels = labels + self.project_description = project_description + self.project_name = project_name + self.root_path = root_path + self.runtime = runtime + + def validate(self): + if self.labels: + for k in self.labels: + if k: + k.validate() + if self.runtime: + self.runtime.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + if self.project_description is not None: + result['ProjectDescription'] = self.project_description + if self.project_name is not None: + result['ProjectName'] = self.project_name + if self.root_path is not None: + result['RootPath'] = self.root_path + if self.runtime is not None: + result['Runtime'] = self.runtime.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = UpdateLLMProjectRequestLabels() + self.labels.append(temp_model.from_map(k)) + if m.get('ProjectDescription') is not None: + self.project_description = m.get('ProjectDescription') + if m.get('ProjectName') is not None: + self.project_name = m.get('ProjectName') + if m.get('RootPath') is not None: + self.root_path = m.get('RootPath') + if m.get('Runtime') is not None: + temp_model = UpdateLLMProjectRequestRuntime() + self.runtime = temp_model.from_map(m['Runtime']) + return self + + +class UpdateLLMProjectResponseBody(TeaModel): + def __init__( + self, + project_id: str = None, + request_id: str = None, + ): + self.project_id = project_id + self.request_id = request_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.project_id is not None: + result['ProjectId'] = self.project_id + if self.request_id is not None: + result['RequestId'] = self.request_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('ProjectId') is not None: + self.project_id = m.get('ProjectId') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + return self + + +class UpdateLLMProjectResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: UpdateLLMProjectResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = UpdateLLMProjectResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + class UpdateQuotaRequest(TeaModel): def __init__( self, description: str = None, labels: List[Label] = None, + queue_strategy: str = None, ): self.description = description self.labels = labels + self.queue_strategy = queue_strategy def validate(self): if self.labels: @@ -16429,6 +22621,8 @@ def to_map(self): if self.labels is not None: for k in self.labels: result['Labels'].append(k.to_map() if k else None) + if self.queue_strategy is not None: + result['QueueStrategy'] = self.queue_strategy return result def from_map(self, m: dict = None): @@ -16440,6 +22634,8 @@ def from_map(self, m: dict = None): for k in m.get('Labels'): temp_model = Label() self.labels.append(temp_model.from_map(k)) + if m.get('QueueStrategy') is not None: + self.queue_strategy = m.get('QueueStrategy') return self @@ -16449,6 +22645,7 @@ def __init__( quota_id: str = None, request_id: str = None, ): + # Quota Id self.quota_id = quota_id self.request_id = request_id @@ -16488,9 +22685,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -16600,9 +22794,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -16635,9 +22826,13 @@ def from_map(self, m: dict = None): class UpdateResourceGroupRequest(TeaModel): def __init__( self, + description: str = None, + name: str = None, unbind: bool = None, user_vpc: UserVpc = None, ): + self.description = description + self.name = name self.unbind = unbind self.user_vpc = user_vpc @@ -16651,6 +22846,10 @@ def to_map(self): return _map result = dict() + if self.description is not None: + result['Description'] = self.description + if self.name is not None: + result['Name'] = self.name if self.unbind is not None: result['Unbind'] = self.unbind if self.user_vpc is not None: @@ -16659,6 +22858,10 @@ def to_map(self): def from_map(self, m: dict = None): m = m or dict() + if m.get('Description') is not None: + self.description = m.get('Description') + if m.get('Name') is not None: + self.name = m.get('Name') if m.get('Unbind') is not None: self.unbind = m.get('Unbind') if m.get('UserVpc') is not None: @@ -16712,9 +22915,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() @@ -16744,6 +22944,107 @@ def from_map(self, m: dict = None): return self +class UpdateResourceGroupMachineGroupRequest(TeaModel): + def __init__( + self, + name: str = None, + ): + self.name = name + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.name is not None: + result['Name'] = self.name + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Name') is not None: + self.name = m.get('Name') + return self + + +class UpdateResourceGroupMachineGroupResponseBody(TeaModel): + def __init__( + self, + machine_group_id: str = None, + request_id: str = None, + ): + self.machine_group_id = machine_group_id + self.request_id = request_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.machine_group_id is not None: + result['MachineGroupID'] = self.machine_group_id + if self.request_id is not None: + result['RequestId'] = self.request_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('MachineGroupID') is not None: + self.machine_group_id = m.get('MachineGroupID') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + return self + + +class UpdateResourceGroupMachineGroupResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: UpdateResourceGroupMachineGroupResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = UpdateResourceGroupMachineGroupResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + class UpdateTrainingJobLabelsRequestLabels(TeaModel): def __init__( self, @@ -16851,9 +23152,6 @@ def __init__( self.body = body def validate(self): - self.validate_required(self.headers, 'headers') - self.validate_required(self.status_code, 'status_code') - self.validate_required(self.body, 'body') if self.body: self.body.validate() From a01c992d28b3357bf6fcb2db3053edec56316e03 Mon Sep 17 00:00:00 2001 From: yangziming Date: Fri, 22 Mar 2024 10:52:06 +0800 Subject: [PATCH 18/59] feat: estimator and processor support to set environments and requirements --- pai/api/training_job.py | 5 ++++- pai/estimator.py | 40 ++++++++++++++++++++++++++++++++++++++++ pai/processor.py | 14 ++++++++++++++ 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/pai/api/training_job.py b/pai/api/training_job.py index 5c99c08..5e1bdbd 100644 --- a/pai/api/training_job.py +++ b/pai/api/training_job.py @@ -36,7 +36,6 @@ class TrainingJobAPI(WorkspaceScopedResourceAPI): - BACKEND_SERVICE_NAME = ServiceName.PAI_STUDIO _list_method = "list_training_jobs_with_options" @@ -91,6 +90,8 @@ def create( hyperparameters: Optional[Dict[str, Any]] = None, input_channels: Optional[List[Dict[str, Any]]] = None, output_channels: Optional[List[Dict[str, Any]]] = None, + environments: Dict[str, str] = None, + requirements: List[str] = None, labels: Optional[Dict[str, str]] = None, max_running_in_seconds: Optional[int] = None, description: Optional[str] = None, @@ -165,6 +166,8 @@ def create( compute_resource=compute_resource, hyper_parameters=hyper_parameters, input_channels=input_channels, + environments=environments, + python_requirements=requirements, labels=labels, output_channels=output_channels, scheduler=scheduler, diff --git a/pai/estimator.py b/pai/estimator.py index 27806c1..bd0f3aa 100644 --- a/pai/estimator.py +++ b/pai/estimator.py @@ -200,6 +200,8 @@ def __init__( max_run_time: Optional[int] = None, output_path: Optional[str] = None, checkpoints_path: Optional[str] = None, + environments: Optional[Dict[str, str]] = None, + requirements: Optional[List[str]] = None, instance_type: Optional[str] = None, instance_spec: Optional[Dict] = None, resource_id: Optional[Dict] = None, @@ -256,6 +258,14 @@ def __init__( checkpoints_path (str, optional): An OSS URI that stores the checkpoint of the training job. If provided, the OSS URI will be mounted to the directory `/ml/output/checkpoints/`. + environments: A dictionary that maps environment variable names to their values. + This optional field allows you to provide a set of environment variables that will be + applied to the context where the code is executed. + requirements (list, optional): An optional list of strings that specifies the Python + package dependencies with their versions. Each string in the list should be in the format + 'package' or 'package==version'. This is similar to the contents of a requirements.txt file used + in Python projects. If requirements.txt is provided in user code directory, requirements + will override the conflict dependencies directly. instance_type (str, optional): The machine instance type used to run the training job. To view the supported machine instance types, please refer to the document: @@ -274,6 +284,8 @@ def __init__( """ self.hyperparameters = hyperparameters or dict() + self.environments = environments + self.requirements = requirements self.instance_type = instance_type self.instance_spec = instance_spec self.resource_id = resource_id @@ -609,6 +621,8 @@ def __init__( git_config: Optional[Dict[str, str]] = None, job_type: str = JobType.PyTorchJob, hyperparameters: Optional[Dict[str, Any]] = None, + environments: Optional[Dict[str, str]] = None, + requirements: Optional[List[str]] = None, base_job_name: Optional[str] = None, max_run_time: Optional[int] = None, checkpoints_path: Optional[str] = None, @@ -674,6 +688,14 @@ def __init__( hyperparameters used in the training job. The hyperparameters will be stored in the `/ml/input/config/hyperparameters.json` as a JSON dictionary in the training container. + environments: A dictionary that maps environment variable names to their values. + This optional field allows you to provide a set of environment variables that will be + applied to the context where the code is executed. + requirements (list, optional): An optional list of strings that specifies the Python + package dependencies with their versions. Each string in the list should be in the format + 'package' or 'package==version'. This is similar to the contents of a requirements.txt file used + in Python projects. If requirements.txt is provided in user code directory, requirements + will override the conflict dependencies directly. instance_type (str): The machine instance type used to run the training job. To view the supported machine instance types, please refer to the document: @@ -764,6 +786,8 @@ def __init__( super(Estimator, self).__init__( hyperparameters=hyperparameters, + environments=environments, + requirements=requirements, base_job_name=base_job_name, max_run_time=max_run_time, output_path=output_path, @@ -952,6 +976,8 @@ def _fit(self, job_name, inputs: Dict[str, Any] = None): resource_id=self.resource_id, job_name=job_name, hyperparameters=self.hyperparameters, + environments=self.environments, + requirements=self.requirements, max_running_in_seconds=self.max_run_time, input_channels=input_configs, output_channels=output_configs, @@ -1026,6 +1052,8 @@ def __init__( algorithm_provider: Optional[str] = None, algorithm_spec: Optional[Dict[str, Any]] = None, hyperparameters: Optional[Dict[str, Any]] = None, + environments: Optional[Dict[str, str]] = None, + requirements: Optional[List[str]] = None, base_job_name: Optional[str] = None, max_run_time: Optional[int] = None, output_path: Optional[str] = None, @@ -1054,6 +1082,14 @@ def __init__( hyperparameters (dict, optional): A dictionary that represents the hyperparameters used in the training job. Default hyperparameters will be retrieved from the algorithm definition. + environments: A dictionary that maps environment variable names to their values. + This optional field allows you to provide a set of environment variables that will be + applied to the context where the code is executed. + requirements (list, optional): An optional list of strings that specifies the Python + package dependencies with their versions. Each string in the list should be in the format + 'package' or 'package==version'. This is similar to the contents of a requirements.txt file used + in Python projects. If requirements.txt is provided in user code directory, requirements + will override the conflict dependencies directly. base_job_name (str, optional): The base name used to generate the training job name. If not provided, a default job name will be generated. max_run_time (int, optional): The maximum time in seconds that the training @@ -1114,6 +1150,8 @@ def __init__( instance_type = self._get_default_training_instance_type() super(AlgorithmEstimator, self).__init__( hyperparameters=self._get_hyperparameters(hyperparameters), + environments=environments, + requirements=requirements, base_job_name=base_job_name, max_run_time=max_run_time, output_path=output_path, @@ -1352,6 +1390,8 @@ def _fit(self, job_name, inputs: Dict[str, Any] = None): max_running_in_seconds=self.max_run_time, input_channels=input_configs, output_channels=output_configs, + environments=self.environments, + requirements=self.requirements, algorithm_name=self.algorithm_name, algorithm_version=self.algorithm_version, algorithm_provider=self.algorithm_provider, diff --git a/pai/processor.py b/pai/processor.py index 0b2f23a..30105ca 100644 --- a/pai/processor.py +++ b/pai/processor.py @@ -142,6 +142,8 @@ def __init__( source_dir: Optional[str] = None, job_type: str = JobType.PyTorchJob, parameters: Optional[Dict[str, Any]] = None, + environments: Optional[Dict[str, str]] = None, + requirements: Optional[List[str]] = None, max_run_time: Optional[int] = None, base_job_name: Optional[str] = None, output_path: Optional[str] = None, @@ -199,6 +201,14 @@ def __init__( parameters used in the job. The parameters will be stored in the `/ml/input/config/hyperparameters.json` as a JSON dictionary in the container. + environments: A dictionary that maps environment variable names to their values. + This optional field allows you to provide a set of environment variables that will be + applied to the context where the code is executed. + requirements (list, optional): An optional list of strings that specifies the Python + package dependencies with their versions. Each string in the list should be in the format + 'package' or 'package==version'. This is similar to the contents of a requirements.txt file used + in Python projects. If requirements.txt is provided in user code directory, requirements + will override the conflict dependencies directly. max_run_time (int, optional): The maximum time in seconds that the job can run. The job will be terminated after the time is reached (Default None). @@ -262,6 +272,8 @@ def __init__( self.source_dir = source_dir self.job_type = job_type or JobType.PyTorchJob self.parameters = parameters or dict() + self.environments = environments + self.requirements = requirements self.max_run_time = max_run_time self.base_job_name = base_job_name @@ -363,6 +375,8 @@ def _fit( instance_type=self.instance_type, job_name=job_name, hyperparameters=self.parameters, + environments=self.environments, + requirements=self.requirements, max_running_in_seconds=self.max_run_time, input_channels=input_configs, output_channels=output_configs, From 8534dff5a52754ca83ea7fc4bb4bde0a2c610c28 Mon Sep 17 00:00:00 2001 From: lq262469 Date: Thu, 18 Apr 2024 16:11:22 +0800 Subject: [PATCH 19/59] feat: increase service gateway readiness threshold * increase service gateway readiness threshold. * create openai API client from predictor. --- pai/common/utils.py | 6 +++ pai/estimator.py | 90 ++++++++++++++++++++++----------------------- pai/predictor.py | 48 ++++++++++++++++++++---- 3 files changed, 91 insertions(+), 53 deletions(-) diff --git a/pai/common/utils.py b/pai/common/utils.py index e6dddb0..4ec74b2 100644 --- a/pai/common/utils.py +++ b/pai/common/utils.py @@ -15,6 +15,7 @@ from __future__ import absolute_import import functools +import importlib.util import random import re import socket @@ -316,3 +317,8 @@ def print_table(headers: List[str], rows: List[List[str]]): f"{str(value):<{column_widths[i]}}" for i, value in enumerate(row) ) ) + + +def is_package_available(package_name: str) -> bool: + """Check if the package is available in the current environment.""" + return True if importlib.util.find_spec(package_name) is not None else False diff --git a/pai/estimator.py b/pai/estimator.py index bd0f3aa..f62c81f 100644 --- a/pai/estimator.py +++ b/pai/estimator.py @@ -509,6 +509,51 @@ def tensorboard_data(self) -> str: channel_name=DEFAULT_TENSORBOARD_CHANNEL_NAME, ) + def tensorboard(self, wait=True): + """Launch a TensorBoard Application to view the output TensorBoard logs. + + Args: + wait (bool): Specifies whether to block until the TensorBoard is running. + + Returns: + :class:`pai.tensorboard.TensorBoard`: A TensorBoard instance. + """ + from pai.tensorboard import TensorBoard + + if not self.latest_training_job: + raise RuntimeError("Could not find a submitted training job.") + + source_type = "TrainingJob" + if isinstance(self.latest_training_job, _LocalTrainingJob): + raise RuntimeError("Local training job does not support tensorboard.") + res = self.session.tensorboard_api.list( + source_type=source_type, + source_id=self.latest_training_job.training_job_id, + ) + + if res.items: + if len(res.items) > 1: + logger.warning( + "Found multiple TensorBoard instances for the submitted training " + "job, use the first one." + ) + tb_id = res.items[0]["TensorboardId"] + tb = TensorBoard(tensorboard_id=tb_id, session=self.session) + tb.start(wait=wait) + else: + tb = TensorBoard.create( + uri=self.tensorboard_data(), + wait=wait, + display_name=self._latest_training_job.training_job_name, + source_id=self.latest_training_job.training_job_id, + source_type=source_type, + session=self.session, + ) + + # Open the TensorBoard in the default browser. + webbrowser.open(tb.app_uri) + return tb + def create_model(self, inference_spec: Union[InferenceSpec, Dict]) -> Model: """Create a Model object using output model of the training job. @@ -913,51 +958,6 @@ def fit( if wait: self.wait(show_logs=show_logs) - def tensorboard(self, wait=True): - """Launch a TensorBoard Application to view the output TensorBoard logs. - - Args: - wait (bool): Specifies whether to block until the TensorBoard is running. - - Returns: - :class:`pai.tensorboard.TensorBoard`: A TensorBoard instance. - """ - from pai.tensorboard import TensorBoard - - if not self.latest_training_job: - raise RuntimeError("Could not find a submitted training job.") - - source_type = "TrainingJob" - if isinstance(self.latest_training_job, _LocalTrainingJob): - raise RuntimeError("Local training job does not support tensorboard.") - res = self.session.tensorboard_api.list( - source_type=source_type, - source_id=self.latest_training_job.training_job_id, - ) - - if res.items: - if len(res.items) > 1: - logger.warning( - "Found multiple TensorBoard instances for the submitted training " - "job, use the first one." - ) - tb_id = res.items[0]["TensorboardId"] - tb = TensorBoard(tensorboard_id=tb_id, session=self.session) - tb.start(wait=wait) - else: - tb = TensorBoard.create( - uri=self.tensorboard_data(), - wait=wait, - display_name=self._latest_training_job.training_job_name, - source_id=self.latest_training_job.training_job_id, - source_type=source_type, - session=self.session, - ) - - # Open the TensorBoard in the default browser. - webbrowser.open(tb.app_uri) - return tb - def _fit(self, job_name, inputs: Dict[str, Any] = None): input_configs = self._build_input_data_configs(inputs) output_configs = self._build_output_data_configs( diff --git a/pai/predictor.py b/pai/predictor.py index fc2cb88..bbd8d5c 100644 --- a/pai/predictor.py +++ b/pai/predictor.py @@ -30,7 +30,7 @@ from .common.consts import FrameworkTypes from .common.docker_utils import ContainerRun -from .common.utils import http_user_agent +from .common.utils import http_user_agent, is_package_available from .exception import PredictionException from .serializers import ( JsonSerializer, @@ -40,6 +40,10 @@ ) from .session import Session, get_default_session +if is_package_available("openai"): + from openai import OpenAI + + logger = logging.getLogger(__name__) _PAI_SERVICE_CONSOLE_URI_PATTERN = ( @@ -332,19 +336,25 @@ def _wait_for_gateway_ready(self, attempts: int = 30, interval: int = 2): interval (int): Interval between each attempt. """ - def _is_gateway_not_ready(resp: requests.Response): - return resp.status_code == 503 and resp.content == b"no healthy upstream" + def _is_gateway_ready(): + resp = self._send_request(method="HEAD") + return not ( + # following status code and content indicate the gateway is not ready + resp.status_code == 503 + and b"no healthy upstream" in resp.content + ) + ready_count_threshold = 3 + ready_count = 0 err_count_threshold = 3 err_count = 0 while attempts > 0: attempts -= 1 try: - # Send a probe request to the service. - resp = self._send_request(method="GET") - if not _is_gateway_not_ready(resp): - logger.info("Gateway for the service is ready.") - break + if _is_gateway_ready(): + ready_count += 1 + if ready_count >= ready_count_threshold: + break except requests.exceptions.RequestException as e: err_count += 1 if err_count >= err_count_threshold: @@ -703,6 +713,28 @@ def raw_predict( ) return resp + def openai(self, **kwargs) -> "OpenAI": + """Initialize an OpenAI client from the predictor. + + Only used for OpenAI API compatible services, such as Large Language Model + service from PAI QuickStart. + + Args: + **kwargs: Additional keyword arguments for the OpenAI client. + + Returns: + OpenAI: An OpenAI client object. + """ + + if not is_package_available("openai"): + raise ImportError( + "openai package is not installed, install it with `pip install openai`." + ) + base_url = kwargs.pop("base_url", self.endpoint + "/v1/") + api_key = kwargs.pop("api_key", self.access_token) + + return OpenAI(base_url=base_url, api_key=api_key, **kwargs) + class WaitConfig(object): """WaitConfig is used to set polling configurations for waiting for asynchronous From 47b45d9f975fc2af0412386930fd93b9d1f3cbfc Mon Sep 17 00:00:00 2001 From: LiangQuan Date: Mon, 22 Apr 2024 11:44:32 +0800 Subject: [PATCH 20/59] release: 0.4.6 (#13) --- pai/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pai/version.py b/pai/version.py index 00d3243..c95ecfb 100644 --- a/pai/version.py +++ b/pai/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -VERSION = "0.4.5.post1" +VERSION = "0.4.6" From 0f7d2e5f4c7fc72b35637cb4a3d0a233e051820d Mon Sep 17 00:00:00 2001 From: "yangyutian.yyt" Date: Wed, 24 Apr 2024 11:21:10 +0800 Subject: [PATCH 21/59] feat: implement experiment feature with api, estimator, and processor * feat: support experiment * feat: add more API and testcase of experiment * feat: add experiment_config for processor * fix: update experiment.list return and its testcase * feat: add estimator with experiment testcase and bugfix * feat: adjust output channel logic of estimator and related testcase * fix: add result check in processor test and addd experiemnt_config in schema file --- pai/api/api_container.py | 6 + pai/api/base.py | 1 + pai/api/experiment.py | 121 +++++++++ pai/api/training_job.py | 5 + pai/estimator.py | 30 +++ pai/experiment.py | 241 ++++++++++++++++++ .../alibabacloud_paistudio20220112/client.py | 3 +- pai/processor.py | 34 ++- pai/schema/training_job_schema.py | 1 + tests/integration/test_estimator.py | 74 +++++- tests/integration/test_experiment.py | 91 +++++++ tests/integration/test_processor.py | 30 +++ tests/test_data/experiment_train/train.py | 6 + 13 files changed, 618 insertions(+), 25 deletions(-) create mode 100644 pai/api/experiment.py create mode 100644 pai/experiment.py create mode 100644 tests/integration/test_experiment.py create mode 100644 tests/test_data/experiment_train/train.py diff --git a/pai/api/api_container.py b/pai/api/api_container.py index c68894a..6832fc6 100644 --- a/pai/api/api_container.py +++ b/pai/api/api_container.py @@ -21,6 +21,7 @@ from .client_factory import ClientFactory from .code_source import CodeSourceAPI from .dataset import DatasetAPI +from .experiment import ExperimentAPI from .image import ImageAPI from .job import JobAPI from .model import ModelAPI @@ -44,6 +45,7 @@ PAIRestResourceTypes.Pipeline: PipelineAPI, PAIRestResourceTypes.PipelineRun: PipelineRunAPI, PAIRestResourceTypes.TensorBoard: TensorBoardAPI, + PAIRestResourceTypes.Experiment: ExperimentAPI, } @@ -182,3 +184,7 @@ def pipeline_api(self) -> PipelineAPI: @property def pipeline_run_api(self) -> PipelineRunAPI: return self.get_api_by_resource(PAIRestResourceTypes.PipelineRun) + + @property + def experiment_api(self) -> ExperimentAPI: + return self.get_api_by_resource(PAIRestResourceTypes.Experiment) diff --git a/pai/api/base.py b/pai/api/base.py index f19a39d..bfb4d0b 100644 --- a/pai/api/base.py +++ b/pai/api/base.py @@ -51,6 +51,7 @@ class PAIRestResourceTypes(object): Pipeline = "Pipeline" PipelineRun = "PipelineRun" TensorBoard = "TensorBoard" + Experiment = "Experiment" class ResourceAPI(with_metaclass(ABCMeta, object)): diff --git a/pai/api/experiment.py b/pai/api/experiment.py new file mode 100644 index 0000000..a39d718 --- /dev/null +++ b/pai/api/experiment.py @@ -0,0 +1,121 @@ +# Copyright 2023 Alibaba, Inc. or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from typing import List + +from .base import ServiceName, WorkspaceScopedResourceAPI, PaginatedResult + +from ..libs.alibabacloud_aiworkspace20210204.models import ( + Experiment, + CreateExperimentRequest, + CreateExperimentResponseBody, + ListExperimentRequest, + ListExperimentResponseBody, + UpdateExperimentRequest, + SetExperimentLabelsRequest, + LabelInfo, +) + + +class ExperimentAPI(WorkspaceScopedResourceAPI): + BACKEND_SERVICE_NAME = ServiceName.PAI_WORKSPACE + + _get_method = "get_experiment_with_options" + _create_method = "create_experiment_with_options" + _list_method = "list_experiment_with_options" + _update_method = "update_experiment_with_options" + _delete_method = "delete_experiment_with_options" + _set_labels_method = "set_experiment_labels_with_options" + _delete_label_method = "delete_experiment_label_with_options" + + def get(self, experiment_id: str): + resp: Experiment = self._do_request( + method_=self._get_method, experiment_id=experiment_id + ) + return resp.to_map() + + def create( + self, + name, + artifact_uri, + workspace_id, + **kwargs, + ): + request = CreateExperimentRequest( + name=name, + artifact_uri=artifact_uri, + workspace_id=workspace_id, + **kwargs, + ) + resp: CreateExperimentResponseBody = self._do_request( + method_=self._create_method, request=request + ) + return resp.experiment_id + + def list( + self, + name: str = None, + page_size: int = 50, + page_number: int = 1, + order: str = "DESC", + **kwargs, + ) -> PaginatedResult: + workspace_id = kwargs.pop("workspace_id", None) + request = ListExperimentRequest( + name=name, + page_size=page_size, + page_number=page_number, + order=order, + workspace_id=workspace_id, + **kwargs, + ) + resp: ListExperimentResponseBody = self._do_request( + method_=self._list_method, request=request + ) + return self.make_paginated_result(resp) + + def update( + self, + experiment_id: str, + name: str, + **kwargs, + ): + request = UpdateExperimentRequest( + name=name, + **kwargs, + ) + self._do_request( + method_=self._update_method, experiment_id=experiment_id, request=request + ) + return + + def delete(self, experiment_id: str): + self._do_request(method_=self._delete_method, experiment_id=experiment_id) + return + + def set_experiment_labels(self, experiment_id: str, labels: List[LabelInfo]): + request = SetExperimentLabelsRequest( + labels=labels, + ) + self._do_request( + method_=self._set_labels_method, + experiment_id=experiment_id, + request=request, + ) + return + + def delete_experiment_label(self, experiment_id: str, key: str): + self._do_request( + method_=self._delete_label_method, experiment_id=experiment_id, key=key + ) + return diff --git a/pai/api/training_job.py b/pai/api/training_job.py index 5e1bdbd..99729f8 100644 --- a/pai/api/training_job.py +++ b/pai/api/training_job.py @@ -32,6 +32,7 @@ ListTrainingJobLogsRequest, ListTrainingJobLogsResponseBody, ListTrainingJobsRequest, + CreateTrainingJobRequestExperimentConfig, ) @@ -100,6 +101,7 @@ def create( algorithm_provider: Optional[str] = None, algorithm_spec: Optional[Dict[str, Any]] = None, user_vpc_config: Optional[Dict[str, Any]] = None, + experiment_config: Optional[Dict[str, Any]] = None, ) -> str: """Create a TrainingJob.""" if algorithm_spec and ( @@ -175,6 +177,9 @@ def create( training_job_name=job_name, algorithm_spec=algo_spec, user_vpc=CreateTrainingJobRequestUserVpc().from_map(user_vpc_config), + experiment_config=CreateTrainingJobRequestExperimentConfig().from_map( + experiment_config + ), ) resp: CreateTrainingJobResponseBody = self._do_request( diff --git a/pai/estimator.py b/pai/estimator.py index f62c81f..193d03a 100644 --- a/pai/estimator.py +++ b/pai/estimator.py @@ -48,6 +48,7 @@ to_plain_text, ) from .exception import UnexpectedStatusException +from .experiment import ExperimentConfig, Experiment from .model import InferenceSpec, Model, ResourceConfig from .predictor import Predictor from .schema.training_job_schema import TrainingJobSchema @@ -207,6 +208,7 @@ def __init__( resource_id: Optional[Dict] = None, instance_count: Optional[int] = None, user_vpc_config: Optional[UserVpcConfig] = None, + experiment_config: Optional[ExperimentConfig] = None, session: Optional[Session] = None, ): """EstimatorBase constructor. @@ -279,6 +281,11 @@ def __init__( be created and attached to the training job instance, allowing the instance to access the resources within the specified VPC. Default to None. + experiment_config(:class:`pai.estimator.ExperimentConfig`, optional): The + experiment configuration used to construct the relationship between the + training job and the experiment. If provided, the training job will belong + to the specified experiment, in which case the training job will use + artifact_uri of experiment as default output path. Default to None. session (Session, optional): A PAI session instance used for communicating with PAI service. @@ -294,6 +301,7 @@ def __init__( self.base_job_name = base_job_name self.output_path = output_path self.user_vpc_config = user_vpc_config + self.experiment_config = experiment_config self.checkpoints_path = checkpoints_path self.session = session or get_default_session() self._latest_training_job = None @@ -421,6 +429,13 @@ def as_oss_dir_uri(uri: str): # if checkpoint path is provided, use it as the checkpoint channel output. if ch["Name"] == DEFAULT_CHECKPOINT_CHANNEL_NAME and self.checkpoints_path: oss_uri = self.checkpoints_path + elif ( + ch["Name"] == DEFAULT_TENSORBOARD_CHANNEL_NAME + and self.experiment_config + ): + continue + elif not self.output_path and self.experiment_config: + continue else: oss_uri = as_oss_dir_uri( posixpath.join(job_base_output_path, ch["Name"]) @@ -676,6 +691,7 @@ def __init__( instance_type: Optional[str] = None, instance_count: Optional[int] = None, user_vpc_config: Optional[UserVpcConfig] = None, + experiment_config: Optional[ExperimentConfig] = None, resource_id: Optional[str] = None, session: Optional[Session] = None, **kwargs, @@ -762,6 +778,11 @@ def __init__( be created and attached to the training job instance, allowing the instance to access the resources within the specified VPC. Default to None. + experiment_config(:class:`pai.estimator.ExperimentConfig`, optional): The + experiment configuration used to construct the relationship between the + training job and the experiment. If provided, the training job will belong + to the specified experiment, in which case the training job will use + artifact_uri of experiment as default output path. Default to None. output_path (str, optional): An OSS URI to store the outputs of the training jobs. If not provided, an OSS URI will be generated using the default OSS bucket in the session. When the `estimator.fit` method is called, @@ -840,6 +861,7 @@ def __init__( instance_type=instance_type, instance_count=instance_count, user_vpc_config=user_vpc_config, + experiment_config=experiment_config, session=session, ) @@ -985,6 +1007,9 @@ def _fit(self, job_name, inputs: Dict[str, Any] = None): user_vpc_config=self.user_vpc_config.to_dict() if self.user_vpc_config else None, + experiment_config=self.experiment_config.to_dict() + if self.experiment_config + else None, ) training_job = _TrainingJob.get(training_job_id) print( @@ -1399,6 +1424,9 @@ def _fit(self, job_name, inputs: Dict[str, Any] = None): user_vpc_config=self.user_vpc_config.to_dict() if self.user_vpc_config else None, + experiment_config=self.experiment_config.to_dict() + if self.experiment_config + else None, ) training_job = _TrainingJob.get(training_job_id) print( @@ -1734,6 +1762,7 @@ def __init__( input_channels: List[Dict[str, str]] = None, labels: Dict[str, str] = None, max_running_time_in_seconds: int = None, + experiment_config: Dict[str, str] = None, description: str = None, session: Session = None, **kwargs, @@ -1752,6 +1781,7 @@ def __init__( self.instance_type = instance_type self.instance_count = instance_count self.max_running_time_in_seconds = max_running_time_in_seconds + self.experiment_config = experiment_config # Load only fields self.create_time = kwargs.pop("create_time", None) diff --git a/pai/experiment.py b/pai/experiment.py new file mode 100644 index 0000000..be6b5ab --- /dev/null +++ b/pai/experiment.py @@ -0,0 +1,241 @@ +# Copyright 2023 Alibaba, Inc. or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging +import webbrowser + +from typing import Optional, Iterator + +from .session import Session, get_default_session +from .tensorboard import TensorBoard + +logger = logging.getLogger(__name__) + +_default_session = None + + +class ExperimentConfig(object): + """ExperimentConfig is used to configure the experiment to which the job belongs.""" + + def __init__( + self, + experiment_id: str, + ): + """Initialize ExperimentConfig. + Args: + experiment_id (str): Specifies the ID of the experiment that training job instance + belongs to. + """ + self.experiment_id = experiment_id + + def to_dict(self): + return { + "ExperimentId": self.experiment_id, + } + + +class Experiment(object): + """An experiment is a collection of runs. It can be used to compare the + performance of different training jobs. You can compare the metrics of + different training jobs in a same experiment by one single TensorBoard. + + You can create an experiment by calling `Experiment.create`. + + When you create a training job, you can specify the experiment name to + make the relationship between the job and the experiment. In this case, + the training job will use the artifact_uri of experiment as default output + path, so you do not need to specify the output path of the training job + anymore. + + Example: + experiment = Experiment.create( + artifact_uri="oss://bucket/path", + name="my_experiment", + ) + est = Estimator( + source_dir="./train/src/", + command="python train.py", + image_uri = training_image_uri, + instance_type="ecs.c6.xlarge", + hyperparameters={ + "n_estimators": 50 + }, + experiment_config=ExperimentConfig( + experiment_name="my_experiment", + ) + ) + + est.fit(inputs={ + "train": "oss://{YOUR_BUCKET_NAME}/path/to/train-data", + "test": "oss://{YOUR_BUCKET_NAME}/path/to/test-data", + }) + + """ + + def __init__( + self, + experiment_id: str, + name: str, + artifact_uri: str, + session: Optional[Session] = None, + ): + """Experiment constructor. + + Args: + experiment_id (str): The UUID of the experiment. It is generated from the PAI service. + name (str): The name of experiment is unique within the workspace. The experiment name must adhere to + the following naming convention: The maximum length is 63 characters. It must start with an uppercase + or lowercase letter or a number, and may include hyphens(-) and underscores(_). + artifact_uri (str): An OSS URI which is the default base path to store the output of the job in the + experiment, including model files and TensorBoard logs. + session (Session, optional): A PAI session instance used for communicating + with PAI service. + """ + + self.session = session or get_default_session() + self.experiment_id = experiment_id + self.name = name + self.artifact_uri = artifact_uri + self._api_object = session.experiment_api.get(experiment_id) + + @classmethod + def create( + cls, + artifact_uri: str, + name: str, + session: Optional[Session] = None, + ) -> "Experiment": + """Create experiment. + Args: + artifact_uri (str): Specifies an OSS URI to store the output of the job in the experiment. + name (str): The name of the experiment. The name must be unique within the workspace. + session (Session): The session to be used. + Returns: + Experiment: The created experiment. + """ + session = session or get_default_session() + experiment_id = session.experiment_api.create( + artifact_uri=artifact_uri, name=name, workspace_id=session.workspace_id + ) + experiment = Experiment( + experiment_id=experiment_id, + name=name, + artifact_uri=artifact_uri, + session=session, + ) + return experiment + + @classmethod + def get(cls, experiment_id: str) -> "Experiment": + session = get_default_session() + experiment = session.experiment_api.get(experiment_id) + return Experiment( + experiment_id=experiment_id, + name=experiment["Name"], + artifact_uri=experiment["ArtifactUri"], + session=session, + ) + + def update( + self, + name: str, + ) -> "Experiment": + """Update experiment name. + Args: + name (str): New experiment name. + Returns: + Experiment: The updated experiment. + """ + self.session.experiment_api.update(self.experiment_id, name=name) + self.name = name + return self + + def delete(self): + """Delete experiment.""" + self.session.experiment_api.delete(self.experiment_id) + + @classmethod + def list( + cls, + name: str = None, + session: Optional[Session] = None, + ) -> Iterator["Experiment"]: + """List experiments. + Args: + name (str): Filter by name. + session (Session): The session to be used. + Return: + Iterator[Experiment]: Experiment iterator. + """ + session = session or get_default_session() + page_size = 50 + page_number = 1 + while True: + result = session.experiment_api.list( + name=name, + page_size=page_size, + page_number=page_number, + ).items + if not result: + break + + for item in result: + yield cls( + session=session, + experiment_id=item["ExperimentId"], + name=item["Name"], + artifact_uri=item["ArtifactUri"], + ) + page_number += 1 + + def tensorboard_data(self) -> str: + """Output TensorBoard logs path. + + Returns: + str: A string in OSS URI format refers to the tensorboard log of experiment. + """ + return self._api_object.get("TensorboardLogUri") + + def tensorboard(self, wait=True) -> "TensorBoard": + """Launch a TensorBoard instance with the tensorboard logs path from the current experiment. + Args: + wait (bool): Wait for TensorBoard to be ready. + """ + from pai.tensorboard import TensorBoard + + source_type = "experiment" + res = self.session.tensorboard_api.list( + source_type=source_type, + source_id=self.experiment_id, + ) + + if res.items: + if len(res.items) > 1: + logger.warning( + "Found multiple TensorBoard instances for the experiment, use the first one." + ) + tb_id = res.items[0]["TensorboardId"] + tb = TensorBoard(tb_id, session=self.session) + tb.start(wait=wait) + else: + tb = TensorBoard.create( + uri=self.tensorboard_data(), + wait=wait, + display_name=self.name + "_TensorBoard", + source_type=source_type, + source_id=self.experiment_id, + session=self.session, + ) + webbrowser.open(tb.app_uri) + return tb diff --git a/pai/libs/alibabacloud_paistudio20220112/client.py b/pai/libs/alibabacloud_paistudio20220112/client.py index 0b61fc4..b89d438 100644 --- a/pai/libs/alibabacloud_paistudio20220112/client.py +++ b/pai/libs/alibabacloud_paistudio20220112/client.py @@ -7,10 +7,11 @@ from alibabacloud_tea_openapi import models as open_api_models from alibabacloud_tea_util.client import Client as UtilClient from alibabacloud_endpoint_util.client import Client as EndpointUtilClient -from pai.libs.alibabacloud_paistudio20220112 import models as pai_studio_20220112_models +# from alibabacloud_paistudio20220112 import models as pai_studio_20220112_models from alibabacloud_tea_util import models as util_models from alibabacloud_openapi_util.client import Client as OpenApiUtilClient +from pai.libs.alibabacloud_paistudio20220112 import models as pai_studio_20220112_models class Client(OpenApiClient): """ diff --git a/pai/processor.py b/pai/processor.py index 30105ca..c23d073 100644 --- a/pai/processor.py +++ b/pai/processor.py @@ -19,7 +19,7 @@ from typing import Any, Dict, List, Optional, Union from .common.configs import UserVpcConfig -from .common.consts import DefaultChannelName, JobType, StoragePathCategory +from .common.consts import JobType, StoragePathCategory from .common.oss_utils import OssUriObj, is_oss_uri, upload from .common.utils import ( experimental, @@ -31,6 +31,7 @@ ) from .estimator import FileSystemInputBase from .estimator import _TrainingJob as _Job +from .experiment import ExperimentConfig from .session import Session, get_default_session logger = logging.getLogger(__name__) @@ -150,6 +151,7 @@ def __init__( instance_type: Optional[str] = None, instance_count: Optional[int] = None, user_vpc_config: Optional[UserVpcConfig] = None, + experiment_config: Optional[ExperimentConfig] = None, session: Optional[Session] = None, ): """Processor constructor. @@ -180,21 +182,6 @@ def __init__( if you need 'src' directory as the source code directory, you can assign source_dir='./src/'. - - code_dir (dict, optional): The "code_dir" object holds the configuration - details for the code location on OSS.If 'code_dir' is provided, - Processor will use it directly and ignore 'source_dir' as well as 'git_config'. - - Example:: - - { - "LocationValue": { - "Bucket": "pai-quickstart-predeploy-hangzhou", - "Key": "/tmp/mock_llm_evaluation/", - "Endpoint": "oss-cn-hangzhou.aliyuncs.com" - }, - "LocationType": "oss" - } job_type (str): The type of job, which can be TFJob, PyTorchJob, XGBoostJob, etc. Default value is PyTorchJob. parameters (dict, optional): A dictionary that represents the @@ -262,7 +249,11 @@ def __init__( be created and attached to the job instance, allowing the instance to access the resources within the specified VPC. Default to None. - + experiment_config(:class:`pai.estimator.ExperimentConfig`, optional): The + experiment configuration used to construct the relationship between the + job and the experiment. If provided, the training job will belong to the + specified experiment, in which case the job will use artifact_uri of + experiment as default output path. Default to None. session (Session, optional): A PAI session instance used for communicating with PAI service. @@ -282,6 +273,7 @@ def __init__( self.instance_type = instance_type self.instance_count = instance_count or 1 self.user_vpc_config = user_vpc_config + self.experiment_config = experiment_config self.session = session or get_default_session() self._latest_job = None @@ -357,7 +349,6 @@ def _build_algorithm_spec(self, code_input) -> Dict[str, Any]: def _fit( self, job_name, inputs: Dict[str, Any] = None, outputs: Dict[str, Any] = None ): - output_path = self._get_job_base_output_path(job_name) upload_path = Session.get_storage_path_by_category( StoragePathCategory.ProcessingSrc, to_plain_text(job_name) @@ -381,7 +372,12 @@ def _fit( input_channels=input_configs, output_channels=output_configs, algorithm_spec=algo_spec, - user_vpc_config=self.ut() if self.user_vpc_config else None, + user_vpc_config=self.user_vpc_config.to_dict() + if self.user_vpc_config + else None, + experiment_config=self.experiment_config.to_dict() + if self.experiment_config + else None, ) job = _Job.get(job_id) print(f"View the job {job_id} by accessing the console URI: {job.console_uri}") diff --git a/pai/schema/training_job_schema.py b/pai/schema/training_job_schema.py index 0b1a28b..27b99f9 100644 --- a/pai/schema/training_job_schema.py +++ b/pai/schema/training_job_schema.py @@ -86,6 +86,7 @@ class TrainingJobSchema(BaseAPIResourceSchema): scheduler = fields.Dict() compute_resource = fields.Dict() workspace_id = fields.Str() + experiment_config = fields.Dict() # load only fields latest_metrics = fields.List(fields.Dict) diff --git a/tests/integration/test_estimator.py b/tests/integration/test_estimator.py index 2aab717..9b461cb 100644 --- a/tests/integration/test_estimator.py +++ b/tests/integration/test_estimator.py @@ -13,12 +13,16 @@ # limitations under the License. import os +import re +import time from unittest import skipUnless import pytest from pai.common.oss_utils import upload +from pai.common.utils import random_str from pai.estimator import AlgorithmEstimator, Estimator +from pai.experiment import ExperimentConfig, Experiment from pai.image import retrieve from pai.session import get_default_session from tests.integration import BaseIntegTestCase @@ -222,11 +226,7 @@ def test_remote_data(self): ) class TestEstimatorLocalRunGPU(BaseIntegTestCase): def test(self): - image_uri = retrieve( - "pytorch", - "1.12", - accelerator_type="GPU", - ).image_uri + image_uri = retrieve("xgboost", framework_version="latest").image_uri est = Estimator( image_uri=image_uri, @@ -235,3 +235,67 @@ def test(self): instance_type="local_gpu", ) est.fit() + + +class TestTrainWithExperimentConfig(BaseIntegTestCase): + def setUp(self): + exp_name = f"sdk_estimator_test_{random_str(6)}" + self.experiment = Experiment.create( + artifact_uri="oss://{}/sdktest/test_experiment/sdk_estimator_test_experiment/".format( + self.default_session.oss_bucket.bucket_name + ), + name=exp_name, + ) + self.image_uri = retrieve( + "pytorch", + "1.12", + accelerator_type="GPU", + ).image_uri + self.command = "python train.py" + self.source_dir = os.path.join(test_data_dir, "experiment_train") + self.instance_type = "ecs.c6.large" + tensorboard_data_escaped = re.escape(self.experiment.tensorboard_data()) + self.tensorboard_path_regex_pattern = f"^{tensorboard_data_escaped}[a-z0-9]+/$" + + def test_train_with_experiment_config(self): + est = Estimator( + image_uri=self.image_uri, + command=self.command, + source_dir=self.source_dir, + instance_type=self.instance_type, + experiment_config=ExperimentConfig( + experiment_id=self.experiment.experiment_id, + ), + ) + est.fit() + + tensorboard_path = est.tensorboard_data() + self.assertRegex(tensorboard_path, self.tensorboard_path_regex_pattern) + artifact_uri_escaped = re.escape(self.experiment.artifact_uri) + model_path_regex_pattern = f"^{artifact_uri_escaped}[a-z0-9]+/model/$" + self.assertRegex(est.model_data(), model_path_regex_pattern) + + def test_train_with_output_and_experiment_config(self): + output_path = "oss://{}/sdktest/test_experiment/output_config_path/".format( + self.default_session.oss_bucket.bucket_name + ) + est = Estimator( + image_uri=self.image_uri, + command=self.command, + source_dir=self.source_dir, + instance_type=self.instance_type, + output_path=output_path, + experiment_config=ExperimentConfig( + experiment_id=self.experiment.experiment_id, + ), + ) + est.fit() + + output_escaped = re.escape(output_path) + model_path_regex_pattern = f"^{output_escaped}[a-z0-9_]+/model/$" + self.assertRegex(est.model_data(), model_path_regex_pattern) + tensorboard_path = est.tensorboard_data() + self.assertRegex(tensorboard_path, self.tensorboard_path_regex_pattern) + + def tearDown(self): + self.experiment.delete() diff --git a/tests/integration/test_experiment.py b/tests/integration/test_experiment.py new file mode 100644 index 0000000..0159ad8 --- /dev/null +++ b/tests/integration/test_experiment.py @@ -0,0 +1,91 @@ +# Copyright 2023 Alibaba, Inc. or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import time + +from pai.experiment import Experiment +from tests.integration import BaseIntegTestCase +from pai.tensorboard import TensorBoardStatus + +tensorboard_path_suffix = "tensorboard/" + + +class TestExperiment(BaseIntegTestCase): + def setUp(self): + super(TestExperiment, self).setUp() + self.artifact_uri = "oss://{}/sdktest/test_experiment/".format( + self.default_session.oss_bucket.bucket_name + ) + + def test_create(self): + # Init test data + exp_name = "test_experiment_" + str(int(time.time())) + # Test create + self.experiment = Experiment.create( + artifact_uri=self.artifact_uri, + name=exp_name, + ) + self.assertEqual(self.experiment.name, exp_name) + expected_tb_path = self.artifact_uri + tensorboard_path_suffix + self.assertEqual(self.experiment.tensorboard_data(), expected_tb_path) + + def test_update(self): + exp_name = "test_experiment_" + str(int(time.time())) + self.experiment = Experiment.create( + artifact_uri=self.artifact_uri, + name=exp_name, + ) + # Test update + exp_name = exp_name + "_updated" + self.experiment.update(name=exp_name) + self.assertEqual(self.experiment.name, exp_name) + + def test_list(self): + exp_name = "test_experiment_" + str(int(time.time())) + self.experiment = Experiment.create( + artifact_uri=self.artifact_uri, + name=exp_name, + ) + # Test list + experiment_iterator = Experiment.list(name=exp_name) + experiment_names = [e.name for e in experiment_iterator] + self.assertEqual(len(experiment_names), 1) + self.assertEqual(experiment_names[0], exp_name) + + def test_get(self): + exp_name = "test_experiment_" + str(int(time.time())) + self.experiment = Experiment.create( + artifact_uri=self.artifact_uri, + name=exp_name, + ) + # Test get + exp1 = Experiment.get(experiment_id=self.experiment.experiment_id) + self.assertEqual(self.experiment.name, exp1.name) + self.assertEqual(self.experiment.experiment_id, exp1.experiment_id) + self.assertEqual(self.experiment.tensorboard_data(), exp1.tensorboard_data()) + + def test_tensorboard(self): + exp_name = "test_experiment_" + str(int(time.time())) + self.experiment = Experiment.create( + artifact_uri=self.artifact_uri, + name=exp_name, + ) + # Test tensorboard + tb = self.experiment.tensorboard() + self.assertIsNotNone(tb.app_uri) + self.assertEqual(tb.status, TensorBoardStatus.Running) + tb.stop() + + def tearDown(self): + if self.experiment: + self.experiment.delete() diff --git a/tests/integration/test_processor.py b/tests/integration/test_processor.py index fe7343c..98e5ab5 100644 --- a/tests/integration/test_processor.py +++ b/tests/integration/test_processor.py @@ -14,6 +14,8 @@ import os +from pai.common.utils import random_str +from pai.experiment import Experiment, ExperimentConfig from pai.image import retrieve from pai.processor import Processor from pai.session import get_default_session @@ -57,3 +59,31 @@ def test_processing_run(self): success_flag = os.path.join(self.processing_output_uri, "output.txt") self.assertIsNotNone(self.is_oss_object_exists(success_flag)) + + def test_train_with_experiment_config(self): + exp_name = f"sdk_estimator_test_{random_str(6)}" + self.experiment = Experiment.create( + artifact_uri="oss://{}/sdktest/test_experiment/sdk_estimator_test_experiment/".format( + self.default_session.oss_bucket.bucket_name + ), + name=exp_name, + ) + + image_uri = retrieve("pytorch", framework_version="1.12").image_uri + processor = Processor( + image_uri=image_uri, + source_dir=SCRIPT_DIR_PATH, + command="python main.py --output_path=/ml/output/flag", + instance_type="ecs.c6.large", + base_job_name="processing", + experiment_config=ExperimentConfig(self.experiment.experiment_id), + ) + + processor.run( + inputs={"test": self.breast_cancer_test_data_uri}, + outputs={"flag": self.processing_output_uri}, + ) + + self.assertIsNotNone(processor.latest_job) + self.assertIsNotNone(processor.latest_job.training_job_name) + self.assertIsNotNone(processor.latest_job.experiment_config) diff --git a/tests/test_data/experiment_train/train.py b/tests/test_data/experiment_train/train.py new file mode 100644 index 0000000..98ce5a7 --- /dev/null +++ b/tests/test_data/experiment_train/train.py @@ -0,0 +1,6 @@ +def run(): + print("hello world") + + +if __name__ == "__main__": + run() From 45f6f3918c855b4c17a45f89fcdbc18286fca0c6 Mon Sep 17 00:00:00 2001 From: LiangQuan Date: Fri, 26 Apr 2024 14:00:29 +0800 Subject: [PATCH 22/59] feat: enhance CI/CD, add features and fixes to improve model deployment and training * add GitHub Actions workflow for release triggering * fix lint error * update to development version * enable labels supports for training job * feat: supports list[str] for job command * docs: switch README.md to Chinese * fix predictor.wait_for_ready * fix model data configuration in model deployment * add credential source hint in pai.toolkit.config * Add labels for training_job from QuickStart model * fix unit test error * update release trigger * support label in model deploy --- .github/workflows/release_trigger.yaml | 41 +++++++++ README.md | 71 ++++++++------- README_CN.md | 80 ----------------- README_EN.md | 98 ++++++++++++++++++++ pai/api/experiment.py | 9 +- pai/api/training_job.py | 2 +- pai/estimator.py | 28 ++++-- pai/experiment.py | 3 +- pai/model.py | 118 ++++++++++++++++++++----- pai/modelscope/model.py | 1 + pai/predictor.py | 22 ++--- pai/processor.py | 25 ++++-- pai/toolkit/config.py | 72 ++++++++++++++- pai/version.py | 2 +- tests/integration/test_estimator.py | 3 +- tests/integration/test_experiment.py | 2 +- tests/integration/test_model.py | 16 +++- tests/unit/test_inference_spec.py | 39 +++++--- 18 files changed, 445 insertions(+), 187 deletions(-) create mode 100644 .github/workflows/release_trigger.yaml delete mode 100644 README_CN.md create mode 100644 README_EN.md diff --git a/.github/workflows/release_trigger.yaml b/.github/workflows/release_trigger.yaml new file mode 100644 index 0000000..f3a367a --- /dev/null +++ b/.github/workflows/release_trigger.yaml @@ -0,0 +1,41 @@ +name: Release Trigger +on: + pull_request: + types: [closed] + branches: + - master + paths: + - 'pai/version.py' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + publish: + name: Release Trigger + runs-on: ubuntu-latest + if: github.event.pull_request.merged == true && startsWith(github.head_ref, 'releases/v') + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: actions/checkout@v4 + - name: Set up Python 3.8 + uses: actions/setup-python@v5 + with: + python-version: '3.8' + - name: Check version match + id: check_version + run: | + BRANCH_VERSION=${{ github.head_ref }} + BRANCH_VERSION=${BRANCH_VERSION#releases/v} + FILE_VERSION=$(python -c "from pai.version import VERSION; print(VERSION)") + if [[ "$BRANCH_VERSION" != "$FILE_VERSION" ]]; then + echo "Version in branch name ($BRANCH_VERSION) does not match version in file ($FILE_VERSION)" + exit 1 + fi + - name: Get version and create version tag + run: | + VERSION=$(python -c "from pai.version import VERSION; print(VERSION)") + git tag v$VERSION + git push origin v$VERSION diff --git a/README.md b/README.md index 31275c7..dd7e92d 100644 --- a/README.md +++ b/README.md @@ -1,84 +1,95 @@ # PAI Python SDK +[English](./README_CN.md) \| 简体中文 -English \| [简体中文](./README_CN.md) +PAI Python SDK是阿里云 [机器学习平台 PAI(Platform for Artificial Intelligence)](https://www.aliyun.com/product/bigdata/learn) 提供的Python SDK,提供了更易用的HighLevel API,支持机器学习工程师简单地使用Python在PAI完成模型训练和部署,串联机器学习的流程。 -The PAI Python SDK is provided by Alibaba Cloud\'s [Platform for Artificial Intelligence (PAI)](https://www.aliyun.com/product/bigdata/learn). It offers a user-friendly High-Level API, enabling machine learning engineers to easily train and deploy models on PAI using Python, streamlining the machine learning workflow. +## 🔧 安装 -## Installation 🔧 - -Install the PAI Python SDK using the following command, which supports Python versions \>= 3.6 (it is recommended to use Python \>= 3.8): +使用以下命令安装PAI Python SDK(支持Python版本 \>= 3.6,建议使用Python版本 \>= 3.8): ```shell python -m pip install alipai ``` -## 📖 Documentation +## 📖 文档 -Find detailed documentation, including API references and user guides, in the [docs](./docs/) directory or visit [PAI Python SDK Documentation](https://alipai.readthedocs.io/). +请通过访问 [PAI Python SDK文档](https://alipai.readthedocs.io/) 或是查看 [docs](./docs) 目录下的文件获取SDK的详细文档,包括用户指南和API文档。 -## 🛠 Basic Usage +## 🛠 使用示例 -- Submit a custom training job +- 提交自定义训练任务 -The following example demonstrates how to submit a custom training job to PAI: +以下代码演示了如何通过SDK提交一个自定义的训练作业: ```python from pai.estimator import Estimator from pai.image import retrieve est = Estimator( - # Retrieve the latest PyTorch image provided by PAI + # 获取PAI提供的最新PyTorch镜像 image_uri=retrieve( framework_name="PyTorch", framework_version="latest" ).image_uri, command="echo hello", - # Optionally, specify the source_dir to upload your training code: + # 可选,指定source_dir上传你的训练代码: # source_dir="./train_src", instance_type="ecs.c6.large", ) - -# Submit the training job +# 提交训练任务 est.fit() - print(est.model_data()) + ``` -- Deploy Large Language Model +- 部署大语言模型 -PAI provides numerous pretrained models that you can easily deploy using the PAI Python SDK: +PAI提供了大量预训练模型,可以使用PAI Python SDK轻松部署: ```python from pai.model import RegisteredModel -# Retrieve the QWen-7b model provided by PAI -qwen_model = RegisteredModel("qwen-7b-chat-lora", model_provider="pai") +# 获取PAI提供的QWen1.5-7b模型 +qwen_model = RegisteredModel("qwen1.5-7b-chat", model_provider="pai") -# Deploy the model +# 部署模型 p = qwen_model.deploy(service_name="qwen_service") -# Call the service +# 调用服务 p.predict( data={ - "prompt": "How to install PyTorch?", - "system_prompt": "Act like you are programmer with 5+ years of experience.", + "prompt": "What is the purpose of life?", + "system_prompt": "You are helpful assistant.", "temperature": 0.8, } ) + +# PAI提供的大语言模型支持OpenAI API,可以通过openai SDK调用 +openai_client = p.openai() +res = openai_client.chat.completions.create( + model="default", + max_tokens=1024, + messages=[ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": "What is the purpose of life?"} + ] +) +print(res.choices[0].message.content) + ``` -For more details, please refer to the [PAI Python SDK Documentation](https://alipai.readthedocs.io/). +更多功能介绍,请参阅 [PAI Python SDK文档](https://alipai.readthedocs.io/) 。 -## 🤝 Contributing +## 🤝 贡献代码 -Contributions to the PAI Python SDK are welcome. Please read our contribution guidelines in the [CONTRIBUTING](./CONTRIBUTING.md) file. +我们欢迎为PAI Python SDK贡献代码。请阅读 [CONTRIBUTING](./CONTRIBUTING.md) 文件了解如何为本项目贡献代码。 -## 📝 License +## 📝 许可证 -PAI Python SDK is developed by Alibaba Cloud and licensed under the Apache License (Version 2.0). +PAI Python SDK是由阿里云开发,并根据Apache许可证(版本2.0)授权使用。 -## 📬 Contact +## 📬 联系方式 -For support or inquiries, please open an issue on the GitHub repository or contact us in the DingTalk group: +如需支持或咨询,请在GitHub仓库中提交issue,或通过钉钉群联系我们: DingTalkGroup diff --git a/README_CN.md b/README_CN.md deleted file mode 100644 index 6afe207..0000000 --- a/README_CN.md +++ /dev/null @@ -1,80 +0,0 @@ -# PAI Python SDK - -[English](./README.md) \| 简体中文 - -PAI Python SDK是阿里云 [机器学习平台 PAI(Platform for Artificial Intelligence)](https://www.aliyun.com/product/bigdata/learn) 提供的Python SDK,提供了更易用的HighLevel API,支持机器学习工程师简单地使用Python在PAI完成模型训练和部署,串联机器学习的流程。 - -## 🔧 安装 - -使用以下命令安装PAI Python SDK(支持Python版本 \>= 3.6,建议使用Python版本 \>= 3.8): - -```shell -python -m pip install alipai -``` - -## 📖 文档 - -请通过访问 [PAI Python SDK文档](https://alipai.readthedocs.io/) 或是查看 [docs](./docs) 目录下的文件获取SDK的详细文档,包括用户指南和API文档。 - -## 🛠 使用示例 - -- 提交自定义训练任务 - -以下代码演示了如何通过SDK提交一个自定义的训练作业: - -```python -from pai.estimator import Estimator -from pai.image import retrieve - -est = Estimator( - # 获取PAI提供的最新PyTorch镜像 - image_uri=retrieve( - framework_name="PyTorch", framework_version="latest" - ).image_uri, - command="echo hello", - # 可选,指定source_dir上传你的训练代码: - # source_dir="./train_src", - instance_type="ecs.c6.large", -) -# 提交训练任务 -est.fit() -print(est.model_data()) - -``` - -- 部署大语言模型 - -PAI提供了大量预训练模型,可以使用PAI Python SDK轻松部署: - -```python -from pai.model import RegisteredModel - -# 获取PAI提供的QWen-7b模型 -qwen_model = RegisteredModel("qwen-7b-chat-lora", model_provider="pai") -# 部署模型 -p = qwen_model.deploy(service_name="qwen_service") -# 调用服务 -p.predict( - data={ - "prompt": "如何安装PyTorch?", - "system_prompt": "表现得像一位有5年以上经验的程序员。", - "temperature": 0.8, - } -) -``` - -更多功能介绍,请参阅 [PAI Python SDK文档](https://alipai.readthedocs.io/) 。 - -## 🤝 贡献代码 - -我们欢迎为PAI Python SDK贡献代码。请阅读 [CONTRIBUTING](./CONTRIBUTING.md) 文件了解如何为本项目贡献代码。 - -## 📝 许可证 - -PAI Python SDK是由阿里云开发,并根据Apache许可证(版本2.0)授权使用。 - -## 📬 联系方式 - -如需支持或咨询,请在GitHub仓库中提交issue,或通过钉钉群联系我们: - -DingTalkGroup diff --git a/README_EN.md b/README_EN.md new file mode 100644 index 0000000..c5df0e8 --- /dev/null +++ b/README_EN.md @@ -0,0 +1,98 @@ +# PAI Python SDK + + +English \| [简体中文](./README.md) + +The PAI Python SDK is provided by Alibaba Cloud\'s [Platform for Artificial Intelligence (PAI)](https://www.aliyun.com/product/bigdata/learn). It offers a user-friendly High-Level API, enabling machine learning engineers to easily train and deploy models on PAI using Python, streamlining the machine learning workflow. + +## Installation 🔧 + +Install the PAI Python SDK using the following command, which supports Python versions \>= 3.6 (it is recommended to use Python \>= 3.8): + +```shell +python -m pip install alipai +``` + +## 📖 Documentation + +Find detailed documentation, including API references and user guides, in the [docs](./docs/) directory or visit [PAI Python SDK Documentation](https://alipai.readthedocs.io/). + +## 🛠 Basic Usage + +- Submit a custom training job + +The following example demonstrates how to submit a custom training job to PAI: + +```python +from pai.estimator import Estimator +from pai.image import retrieve + +est = Estimator( + # Retrieve the latest PyTorch image provided by PAI + image_uri=retrieve( + framework_name="PyTorch", framework_version="latest" + ).image_uri, + command="echo hello", + # Optionally, specify the source_dir to upload your training code: + # source_dir="./train_src", + instance_type="ecs.c6.large", +) + +# Submit the training job +est.fit() + +print(est.model_data()) +``` + +- Deploy Large Language Model + +PAI provides numerous pretrained models that you can easily deploy using the PAI Python SDK: + +```python +from pai.model import RegisteredModel + +# Retrieve the QWen1.5-7b model provided by PAI +qwen_model = RegisteredModel("qwen1.5-7b-chat", model_provider="pai") + +# Deploy the model +p = qwen_model.deploy(service_name="qwen_service") + +# Call the service +p.predict( + data={ + "prompt": "How to install PyTorch?", + "system_prompt": "You are helpful assistant.", + "temperature": 0.8, + } +) + +# Call the LLM service with openai SDK. +openai_client = p.openai() +res = openai_client.chat.completions.create( + model="default", + max_tokens=1024, + messages=[ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": "What is the purpose of life?"} + ] +) +print(res.choices[0].message.content) + + +``` + +For more details, please refer to the [PAI Python SDK Documentation](https://alipai.readthedocs.io/). + +## 🤝 Contributing + +Contributions to the PAI Python SDK are welcome. Please read our contribution guidelines in the [CONTRIBUTING](./CONTRIBUTING.md) file. + +## 📝 License + +PAI Python SDK is developed by Alibaba Cloud and licensed under the Apache License (Version 2.0). + +## 📬 Contact + +For support or inquiries, please open an issue on the GitHub repository or contact us in the DingTalk group: + +DingTalkGroup diff --git a/pai/api/experiment.py b/pai/api/experiment.py index a39d718..07341a3 100644 --- a/pai/api/experiment.py +++ b/pai/api/experiment.py @@ -13,18 +13,17 @@ # limitations under the License. from typing import List -from .base import ServiceName, WorkspaceScopedResourceAPI, PaginatedResult - from ..libs.alibabacloud_aiworkspace20210204.models import ( - Experiment, CreateExperimentRequest, CreateExperimentResponseBody, + Experiment, + LabelInfo, ListExperimentRequest, ListExperimentResponseBody, - UpdateExperimentRequest, SetExperimentLabelsRequest, - LabelInfo, + UpdateExperimentRequest, ) +from .base import PaginatedResult, ServiceName, WorkspaceScopedResourceAPI class ExperimentAPI(WorkspaceScopedResourceAPI): diff --git a/pai/api/training_job.py b/pai/api/training_job.py index 99729f8..25a2b5d 100644 --- a/pai/api/training_job.py +++ b/pai/api/training_job.py @@ -20,6 +20,7 @@ CreateTrainingJobRequest, CreateTrainingJobRequestComputeResource, CreateTrainingJobRequestComputeResourceInstanceSpec, + CreateTrainingJobRequestExperimentConfig, CreateTrainingJobRequestHyperParameters, CreateTrainingJobRequestInputChannels, CreateTrainingJobRequestLabels, @@ -32,7 +33,6 @@ ListTrainingJobLogsRequest, ListTrainingJobLogsResponseBody, ListTrainingJobsRequest, - CreateTrainingJobRequestExperimentConfig, ) diff --git a/pai/estimator.py b/pai/estimator.py index 193d03a..33b6bfd 100644 --- a/pai/estimator.py +++ b/pai/estimator.py @@ -48,7 +48,7 @@ to_plain_text, ) from .exception import UnexpectedStatusException -from .experiment import ExperimentConfig, Experiment +from .experiment import Experiment, ExperimentConfig from .model import InferenceSpec, Model, ResourceConfig from .predictor import Predictor from .schema.training_job_schema import TrainingJobSchema @@ -209,6 +209,7 @@ def __init__( instance_count: Optional[int] = None, user_vpc_config: Optional[UserVpcConfig] = None, experiment_config: Optional[ExperimentConfig] = None, + labels: Optional[Dict[str, str]] = None, session: Optional[Session] = None, ): """EstimatorBase constructor. @@ -281,11 +282,14 @@ def __init__( be created and attached to the training job instance, allowing the instance to access the resources within the specified VPC. Default to None. - experiment_config(:class:`pai.estimator.ExperimentConfig`, optional): The + experiment_config (:class:`pai.estimator.ExperimentConfig`, optional): The experiment configuration used to construct the relationship between the training job and the experiment. If provided, the training job will belong to the specified experiment, in which case the training job will use artifact_uri of experiment as default output path. Default to None. + labels (Dict[str, str], optional): A dictionary that maps label names to + their values. This optional field allows you to provide a set of labels + that will be applied to the training job. session (Session, optional): A PAI session instance used for communicating with PAI service. @@ -304,6 +308,7 @@ def __init__( self.experiment_config = experiment_config self.checkpoints_path = checkpoints_path self.session = session or get_default_session() + self.labels = labels self._latest_training_job = None def set_hyperparameters(self, **kwargs): @@ -676,7 +681,7 @@ class Estimator(EstimatorBase): def __init__( self, image_uri: str, - command: str, + command: Union[str, List[str]], source_dir: Optional[str] = None, git_config: Optional[Dict[str, str]] = None, job_type: str = JobType.PyTorchJob, @@ -703,7 +708,7 @@ def __init__( provided by PAI or a user customized image. To view the images provided by PAI, please refer to the document: https://help.aliyun.com/document_detail/202834.htm. - command (str): The command used to run the training job. + command (Union[str, List[str]]): The command used to run the training job. source_dir (str, optional): The local source code directory used in the training job. The directory will be packaged and uploaded to an OSS bucket, then downloaded to the `/ml/usercode` directory in the training @@ -932,11 +937,15 @@ def _build_algorithm_spec( code_input, ) -> Dict[str, Any]: """Build a temporary AlgorithmSpec used for submitting the TrainingJob.""" - command = [ - "/bin/sh", - "-c", - self.command, - ] + command = ( + self.command + if isinstance(self.command, list) + else [ + "/bin/sh", + "-c", + self.command, + ] + ) algo_spec = { "Command": command, "Image": self.training_image_uri(), @@ -1010,6 +1019,7 @@ def _fit(self, job_name, inputs: Dict[str, Any] = None): experiment_config=self.experiment_config.to_dict() if self.experiment_config else None, + labels=self.labels, ) training_job = _TrainingJob.get(training_job_id) print( diff --git a/pai/experiment.py b/pai/experiment.py index be6b5ab..401d714 100644 --- a/pai/experiment.py +++ b/pai/experiment.py @@ -14,8 +14,7 @@ import logging import webbrowser - -from typing import Optional, Iterator +from typing import Iterator, Optional from .session import Session, get_default_session from .tensorboard import TensorBoard diff --git a/pai/model.py b/pai/model.py index b290641..d2345bc 100644 --- a/pai/model.py +++ b/pai/model.py @@ -23,13 +23,14 @@ import tempfile import textwrap import time +import typing from typing import Any, Dict, Iterator, List, Optional, Tuple, Union import requests from addict import Dict as AttrDict from oss2 import ObjectIterator -from .common import git_utils +from .common import ProviderAlibabaPAI, git_utils from .common.configs import UserVpcConfig from .common.consts import INSTANCE_TYPE_LOCAL_GPU, ModelFormat from .common.docker_utils import ContainerRun, run_container @@ -40,12 +41,15 @@ random_str, to_plain_text, ) -from .exception import DuplicatedMountException, MountPathIsOccupiedException +from .exception import DuplicatedMountException from .image import ImageInfo from .predictor import AsyncPredictor, LocalPredictor, Predictor, ServiceType from .serializers import SerializerBase from .session import Session, get_default_session +if typing.TYPE_CHECKING: + from pai.estimator import AlgorithmEstimator + logger = logging.getLogger(__name__) # Reserved ports for internal use, do not use them for service @@ -285,6 +289,7 @@ def mount( source: str, mount_path: str, session: Session = None, + properties: Optional[Dict[str, Any]] = None, ) -> Dict[str, Any]: """Mount a source storage to the running container. @@ -364,6 +369,9 @@ def mount( "Source path is not a valid OSS URI or a existing local path." ) + if properties: + storage_config.update({"properties": properties}) + # check if the source OSS Path is already mounted to the container. if oss_uri_obj.get_dir_uri() in uris: raise DuplicatedMountException( @@ -375,6 +383,68 @@ def mount( self.storage = configs return storage_config + def set_model_data(self, model_data: str, mount_path: Optional[str] = None): + """ + Set the model data for the InferenceSpec instance. + + Args: + model_data (str): The model data to be set. It must be an OSS URI. + mount_path (str, optional): The mount path in the container. + + Raises: + DuplicatedMountException: If the model data is already mounted to the container. + """ + + def is_model_storage(storage: Dict[str, Any]): + return ( + "properties" in storage + and storage["properties"].get("resource_type") == "model" + ) + + if not model_data: + return + if not self.is_container_serving(): + # if model_data is an OSS URI with endpoint, truncate the endpoint. + oss_uri_obj = OssUriObj(model_data) + model_path_uri = "oss://{bucket_name}/{key}".format( + bucket_name=oss_uri_obj.bucket_name, + key=oss_uri_obj.object_key, + ) + self.add_option("model_path", model_path_uri) + else: + indexes = [idx for idx, s in enumerate(self.storage) if is_model_storage(s)] + # replace the first model storage with the model_data. + if indexes: + if len(indexes) > 1: + logger.warning( + "Multiple model storage found in the InferenceSpec," + " use the first one." + ) + idx = indexes[0] + oss_uri_obj = OssUriObj(model_data) + + storage_config = { + "path": oss_uri_obj.get_dir_uri(), + } + + if oss_uri_obj.endpoint: + storage_config.update( + { + "endpoint": oss_uri_obj.endpoint, + } + ) + self.storage[idx].oss = self._transform_value(storage_config) + else: + try: + self.mount( + model_data, + mount_path=mount_path or DefaultServiceConfig.model_path, + properties={"resource_type": "model", "resource_use": "base"}, + ) + except DuplicatedMountException as e: + # ignore duplicated mount + logger.warning("Model is already mounted the container: %s", e) + def container_serving_spec( command: str, @@ -762,6 +832,7 @@ def deploy( options=options, wait=wait, serializer=serializer, + **kwargs, ) def _generate_service_name(self): @@ -779,6 +850,7 @@ def _deploy( options: Dict[str, Any] = None, wait: bool = True, serializer: "SerializerBase" = None, + labels: Optional[Dict[str, str]] = None, ): """Create a prediction service.""" if not service_name: @@ -797,7 +869,7 @@ def _deploy( resource_id=resource_id, options=options, ) - service_name = self.session.service_api.create(config=config) + service_name = self.session.service_api.create(config=config, labels=labels) self._wait_service_visible(service_name) if service_type == ServiceType.Async: predictor = AsyncPredictor( @@ -866,26 +938,7 @@ def _build_service_config( inference_spec = InferenceSpec( self._get_inference_spec().to_dict() if self.inference_spec else dict() ) - - if self.model_data: - if not inference_spec.is_container_serving(): - # if model_data is an OSS URI with endpoint, truncate the endpoint. - oss_uri_obj = OssUriObj(self.model_data) - model_path_uri = "oss://{bucket_name}/{key}".format( - bucket_name=oss_uri_obj.bucket_name, - key=oss_uri_obj.object_key, - ) - inference_spec.add_option("model_path", model_path_uri) - else: - try: - inference_spec.mount( - self.model_data, - mount_path=DefaultServiceConfig.model_path, - ) - except DuplicatedMountException as e: - # ignore duplicated mount - logger.info("Model is already mounted the container: %s", e) - + inference_spec.set_model_data(model_data=self.model_data) if service_type: inference_spec.add_option("metadata.type", service_type) if inference_spec.is_container_serving(): @@ -1391,6 +1444,7 @@ def __init__( self.model_name = self._model_info.get("ModelName") self.model_provider = self._model_info.get("Provider") self.task = self._model_info.get("Task") + self.domain = self._model_info.get("Domain") self.framework_type = self._model_version_info.get("FrameworkType") self.source_type = self._model_version_info.get("SourceType") self.source_id = self._model_version_info.get("SourceId") @@ -1686,6 +1740,7 @@ def deploy( options=options, wait=wait, serializer=serializer, + **kwargs, ) def _build_service_config( @@ -1767,7 +1822,7 @@ def get_estimator( output_path: Optional[str] = None, max_run_time: Optional[int] = None, **kwargs, - ): + ) -> "AlgorithmEstimator": """Generate an AlgorithmEstimator. Generate an AlgorithmEstimator object from RegisteredModel's training_spec. @@ -1847,6 +1902,20 @@ def get_estimator( ) instance_spec = instance_spec or train_compute_resource.get("InstanceSpec") + labels = kwargs.pop("labels", dict()) + if self.model_provider == ProviderAlibabaPAI: + default_labels = { + "BaseModelUri": self.uri, + "CreatedBy": "QuickStart", + "Domain": self.domain, + "RootModelID": self.model_id, + "RootModelName": self.model_name, + "RootModelVersion": self.model_version, + "Task": self.task, + } + default_labels.update(labels) + labels = default_labels + return AlgorithmEstimator( algorithm_name=algorithm_name, algorithm_version=algorithm_version, @@ -1859,6 +1928,7 @@ def get_estimator( instance_count=instance_count, instance_spec=instance_spec, output_path=output_path, + labels=labels, **kwargs, ) diff --git a/pai/modelscope/model.py b/pai/modelscope/model.py index f74d0e7..2246d4e 100644 --- a/pai/modelscope/model.py +++ b/pai/modelscope/model.py @@ -350,4 +350,5 @@ def deploy( options=options, wait=wait, serializer=serializer, + **kwargs, ) diff --git a/pai/predictor.py b/pai/predictor.py index bbd8d5c..f4f92e2 100644 --- a/pai/predictor.py +++ b/pai/predictor.py @@ -327,7 +327,7 @@ def wait_for_ready(self, force: bool = False): self._wait_for_gateway_ready() self.refresh() - def _wait_for_gateway_ready(self, attempts: int = 30, interval: int = 2): + def _wait_for_gateway_ready(self, attempts: int = 60, interval: int = 2): """Hacky way to wait for the service gateway to be ready. Args: @@ -337,24 +337,24 @@ def _wait_for_gateway_ready(self, attempts: int = 30, interval: int = 2): """ def _is_gateway_ready(): - resp = self._send_request(method="HEAD") - return not ( - # following status code and content indicate the gateway is not ready - resp.status_code == 503 - and b"no healthy upstream" in resp.content + resp = self._send_request(method="GET") + res = not ( + # following status code and content indicates the gateway is not ready + ( + resp.status_code == 503 + and (b"no healthy upstream" in resp.content or not resp.content) + ) + or (resp.status_code == 404 and not resp.content) ) + return res - ready_count_threshold = 3 - ready_count = 0 err_count_threshold = 3 err_count = 0 while attempts > 0: attempts -= 1 try: if _is_gateway_ready(): - ready_count += 1 - if ready_count >= ready_count_threshold: - break + break except requests.exceptions.RequestException as e: err_count += 1 if err_count >= err_count_threshold: diff --git a/pai/processor.py b/pai/processor.py index c23d073..0af0259 100644 --- a/pai/processor.py +++ b/pai/processor.py @@ -139,7 +139,7 @@ class Processor(object): def __init__( self, image_uri: str, - command: str, + command: Union[str, List[str]], source_dir: Optional[str] = None, job_type: str = JobType.PyTorchJob, parameters: Optional[Dict[str, Any]] = None, @@ -152,6 +152,7 @@ def __init__( instance_count: Optional[int] = None, user_vpc_config: Optional[UserVpcConfig] = None, experiment_config: Optional[ExperimentConfig] = None, + labels: Optional[Dict[str, str]] = None, session: Optional[Session] = None, ): """Processor constructor. @@ -161,7 +162,7 @@ def __init__( provided by PAI or a user customized image. To view the images provided by PAI, please refer to the document: https://help.aliyun.com/document_detail/202834.htm. - command (str): The command used to run the job. + command (Union[str, List[str]): The command used to run the job. source_dir (str, optional): The local source code directory used in the job. The directory will be packaged and uploaded to an OSS bucket, then downloaded to the `/ml/usercode` directory in the @@ -254,6 +255,9 @@ def __init__( job and the experiment. If provided, the training job will belong to the specified experiment, in which case the job will use artifact_uri of experiment as default output path. Default to None. + labels (Dict[str, str], optional): A dictionary that maps label names to + their values. This optional field allows you to provide a set of labels + that will be applied to the training job. session (Session, optional): A PAI session instance used for communicating with PAI service. @@ -272,6 +276,7 @@ def __init__( self.instance_type = instance_type self.instance_count = instance_count or 1 + self.labels = labels self.user_vpc_config = user_vpc_config self.experiment_config = experiment_config self.session = session or get_default_session() @@ -330,11 +335,16 @@ def _gen_job_display_name(self, job_name=None): def _build_algorithm_spec(self, code_input) -> Dict[str, Any]: """Build a temporary AlgorithmSpec used for submitting the Job.""" - command = [ - "/bin/sh", - "-c", - self.command, - ] + command = ( + self.command + if isinstance(self.command, list) + else [ + "/bin/sh", + "-c", + self.command, + ] + ) + algo_spec = { "Command": command, "Image": self.image_uri, @@ -378,6 +388,7 @@ def _fit( experiment_config=self.experiment_config.to_dict() if self.experiment_config else None, + labels=self.labels, ) job = _Job.get(job_id) print(f"View the job {job_id} by accessing the console URI: {job.console_uri}") diff --git a/pai/toolkit/config.py b/pai/toolkit/config.py index e4e5b12..b50eb25 100644 --- a/pai/toolkit/config.py +++ b/pai/toolkit/config.py @@ -15,12 +15,21 @@ import locale import logging import os.path +from enum import Enum from typing import Any, Dict, List, Optional import oss2 from alibabacloud_credentials.client import Client as CredentialClient from alibabacloud_credentials.exceptions import CredentialException from alibabacloud_credentials.models import Config as CredentialConfig +from alibabacloud_credentials.providers import ( + EcsRamRoleCredentialProvider, + EnvironmentVariableCredentialsProvider, + OIDCRoleArnCredentialProvider, + ProfileCredentialsProvider, + RamRoleArnCredentialProvider, + RsaKeyPairCredentialProvider, +) from alibabacloud_credentials.utils import auth_constant from oss2.models import SimplifiedBucketInfo from prompt_toolkit import prompt @@ -81,6 +90,61 @@ def _get_default_credential_client() -> Optional[CredentialClient]: logger.debug("Not found credential from default credential provider chain.") +class CredentialProviderType(Enum): + EnvironmentVariable = EnvironmentVariableCredentialsProvider + OIDCRoleArn = OIDCRoleArnCredentialProvider + EcsRamRole = EcsRamRoleCredentialProvider + RamRoleArn = RamRoleArnCredentialProvider + RsaKeyPair = RsaKeyPairCredentialProvider + Profile = ProfileCredentialsProvider + + @classmethod + def get_current_provider(cls) -> Optional["CredentialProviderType"]: + from alibabacloud_credentials.providers import DefaultCredentialsProvider + + d = {t.value: t for t in cls} + provider = DefaultCredentialsProvider() + for p in provider.user_configuration_providers: + if p.get_credentials(): + return d.get(p.__class__) + + def credential_hint(self) -> str: + provider_hints = { + CredentialProviderType.EnvironmentVariable: localized_text( + "The credential source is: Environment Variable", + "凭证来源: 环境变量(ALIBABACLOUD_ACCESS_KEY_ID, ALIBABACLOUD_ACCESS_KEY_SECRET)", + ), + CredentialProviderType.OIDCRoleArn: localized_text( + "The credential source is: OIDC Role Arn", + "凭证来源: OIDC Role Arn", + ), + CredentialProviderType.EcsRamRole: localized_text( + "The credential source is: ECS Ram Role", + "凭证来源: ECS Ram Role", + ), + CredentialProviderType.RamRoleArn: localized_text( + "The credential source is: Ram Role Arn", + "凭证来源: Ram Role Arn", + ), + CredentialProviderType.RsaKeyPair: localized_text( + "The credential source is: RSA Key Pair", + "凭证来源: RSA Key Pair", + ), + CredentialProviderType.Profile: localized_text( + "The credential source is: Profile", + "凭证来源: Profile(~/.alibabacloud/credentials.ini)", + ), + } + + return provider_hints.get( + self, + localized_text( + "The credential source is: Unknown", + "凭证来源: 未知", + ), + ) + + def prompt_for_credential(): default_credential_client = _get_default_credential_client() if not default_credential_client: @@ -123,10 +187,14 @@ def prompt_for_credential(): else: print( localized_text( - "Use credential from default credential provider chain.", - "使用默认的凭证链获取密钥.", + "Use credential from default credential provider chain:", + "使用默认的凭证链获取访问密钥:", ) ) + credential_source_hint = ( + CredentialProviderType.get_current_provider().credential_hint() + ) + print(credential_source_hint) credential_client = default_credential_client credential_config = None diff --git a/pai/version.py b/pai/version.py index c95ecfb..4f2f2ba 100644 --- a/pai/version.py +++ b/pai/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -VERSION = "0.4.6" +VERSION = "0.4.6.dev0" diff --git a/tests/integration/test_estimator.py b/tests/integration/test_estimator.py index 9b461cb..ec0ec98 100644 --- a/tests/integration/test_estimator.py +++ b/tests/integration/test_estimator.py @@ -14,7 +14,6 @@ import os import re -import time from unittest import skipUnless import pytest @@ -22,7 +21,7 @@ from pai.common.oss_utils import upload from pai.common.utils import random_str from pai.estimator import AlgorithmEstimator, Estimator -from pai.experiment import ExperimentConfig, Experiment +from pai.experiment import Experiment, ExperimentConfig from pai.image import retrieve from pai.session import get_default_session from tests.integration import BaseIntegTestCase diff --git a/tests/integration/test_experiment.py b/tests/integration/test_experiment.py index 0159ad8..537f24a 100644 --- a/tests/integration/test_experiment.py +++ b/tests/integration/test_experiment.py @@ -14,8 +14,8 @@ import time from pai.experiment import Experiment -from tests.integration import BaseIntegTestCase from pai.tensorboard import TensorBoardStatus +from tests.integration import BaseIntegTestCase tensorboard_path_suffix = "tensorboard/" diff --git a/tests/integration/test_model.py b/tests/integration/test_model.py index 4305fef..1b038d0 100644 --- a/tests/integration/test_model.py +++ b/tests/integration/test_model.py @@ -90,7 +90,6 @@ def test_container_serving(self): serializer=NumpyBytesSerializer(), ) self.predictors.append(predictor) - # hack: wait for service ready df = pd.read_csv( os.path.join(test_data_dir, "breast_cancer_data/test.csv"), ) @@ -267,6 +266,21 @@ def test_builtin_algo_rm_train(self): ) est = m.get_estimator() + + self.assertEqual( + est.labels.get("BaseModelUri"), + m.uri, + ) + + self.assertEqual( + est.labels.get("RootModelName"), + m.model_name, + ) + self.assertEqual( + est.labels.get("RootModelID"), + m.model_id, + ) + inputs = m.get_estimator_inputs() est.hyperparameters["max_epochs"] = 5 est.hyperparameters["warmup_epochs"] = 2 diff --git a/tests/unit/test_inference_spec.py b/tests/unit/test_inference_spec.py index da31ef4..3d175cc 100644 --- a/tests/unit/test_inference_spec.py +++ b/tests/unit/test_inference_spec.py @@ -12,13 +12,13 @@ # See the License for the specific language governing permissions and # limitations under the License. -from pai.exception import DuplicatedMountException, MountPathIsOccupiedException -from pai.model import InferenceSpec +from pai.exception import DuplicatedMountException +from pai.model import InferenceSpec, container_serving_spec from tests.unit import BaseUnitTestCase class TestInferenceSpec(BaseUnitTestCase): - def test_inference_spec(self): + def test_add_options(self): infer_spec = InferenceSpec( processor="pmml", ) @@ -37,6 +37,10 @@ def test_inference_spec(self): infer_spec.add_option("metadata.rpc.batching", True) self.assertEqual(infer_spec.metadata.rpc.batching, True) + def test_mount_storage(self): + infer_spec = InferenceSpec( + processor="pmml", + ) infer_spec.storage = [ { "mount_path": "/ml/model/", @@ -54,14 +58,6 @@ def test_inference_spec(self): d, { "processor": "pmml", - "metadata": { - "instance": 2, - "rpc": { - "keepalive": 10000, - "batching": True, - }, - }, - "name": "example", "storage": [ { "mount_path": "/ml/model/", @@ -93,3 +89,24 @@ def test_inference_spec(self): infer_spec.mount( "oss://pai-sdk-example/path/to/abc/edfg", mount_path="/ml/code/" ) + + def test_set_model(self): + infer_spec = container_serving_spec( + command="python3 /ml/code/model.py", + image_uri="python:3", + ) + infer_spec.storage = [ + { + "mount_path": "/ml/code/", + "oss": { + "path": "oss://pai-sdk-example/path/to/code/", + }, + }, + ] + model_path_v1 = "oss://pai-sdk-example/path/to/model/v1/" + infer_spec.set_model_data(model_path_v1) + self.assertEqual(model_path_v1, infer_spec.storage[1].oss.path) + + model_path_v2 = "oss://pai-sdk-example/path/to/model/v2/" + infer_spec.set_model_data(model_path_v2) + self.assertEqual(model_path_v2, infer_spec.storage[1].oss.path) From 9e1a08f42bbceeae33d25aba99c684390e5343dc Mon Sep 17 00:00:00 2001 From: LiangQuan Date: Mon, 29 Apr 2024 12:45:39 +0800 Subject: [PATCH 23/59] add labels for service deploy via quickstart model (#16) * add labels for service deploy via QuickStart model * fix base_url for openai client generated via predictor * fix: fix workflow step name * remove integration test in Github workflow * fix missing labels in AlgorithmEstimator.fit --- .github/workflows/integration.yaml | 29 ----------------------------- .github/workflows/unit.yaml | 2 +- noxfile.py | 2 +- pai/estimator.py | 2 ++ pai/model.py | 21 ++++++++++++++++++++- pai/predictor.py | 30 +++++++++++++++++++++++------- pyproject.toml | 2 +- tests/integration/test_model.py | 8 +++++++- 8 files changed, 55 insertions(+), 41 deletions(-) delete mode 100644 .github/workflows/integration.yaml diff --git a/.github/workflows/integration.yaml b/.github/workflows/integration.yaml deleted file mode 100644 index e70a926..0000000 --- a/.github/workflows/integration.yaml +++ /dev/null @@ -1,29 +0,0 @@ -name: Integration test - -on: - push: - branches: - - master - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - integration-test: - runs-on: ubuntu-latest - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - TEST_CONFIG_FILE: ${{ secrets.TEST_CONFIG_FILE }} - steps: - - uses: actions/checkout@v4 - - name: Set up Python 3.8 - uses: actions/setup-python@v5 - with: - python-version: "3.8" - - name: Prepare test configuration - run: echo $TEST_CONFIG_FILE > tests/integration/test.ini - - name: Install Nox - run: pip install nox - - name: Run integration test - run: nox -s integration diff --git a/.github/workflows/unit.yaml b/.github/workflows/unit.yaml index de74d41..9d91f5b 100644 --- a/.github/workflows/unit.yaml +++ b/.github/workflows/unit.yaml @@ -7,7 +7,7 @@ concurrency: cancel-in-progress: true jobs: - lint: + unit-test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/noxfile.py b/noxfile.py index cbba9d7..686429b 100644 --- a/noxfile.py +++ b/noxfile.py @@ -38,7 +38,7 @@ def install_test_dependencies(session: Session): session.install("-r", TEST_REQUIREMENTS) -@nox.session(venv_backend=TEST_VENV_BACKEND, python=INTEGRATION_TEST_PYTHON_VERSIONS) +@nox.session(venv_backend=TEST_VENV_BACKEND) def integration(session: Session): """Run integration test.""" install_test_dependencies(session=session) diff --git a/pai/estimator.py b/pai/estimator.py index 33b6bfd..55887e8 100644 --- a/pai/estimator.py +++ b/pai/estimator.py @@ -867,6 +867,7 @@ def __init__( instance_count=instance_count, user_vpc_config=user_vpc_config, experiment_config=experiment_config, + resource_id=resource_id, session=session, ) @@ -1437,6 +1438,7 @@ def _fit(self, job_name, inputs: Dict[str, Any] = None): experiment_config=self.experiment_config.to_dict() if self.experiment_config else None, + labels=self.labels, ) training_job = _TrainingJob.get(training_job_id) print( diff --git a/pai/model.py b/pai/model.py index d2345bc..2ee1e08 100644 --- a/pai/model.py +++ b/pai/model.py @@ -55,6 +55,9 @@ # Reserved ports for internal use, do not use them for service _RESERVED_PORTS = [8080, 9090] +# Default model upstream source +MODEL_TASK_CREATED_BY_QUICKSTART = "QuickStart" + class DefaultServiceConfig(object): """Default configuration used in creating prediction service.""" @@ -851,6 +854,7 @@ def _deploy( wait: bool = True, serializer: "SerializerBase" = None, labels: Optional[Dict[str, str]] = None, + **kwargs, ): """Create a prediction service.""" if not service_name: @@ -1723,6 +1727,20 @@ def deploy( if not self.inference_spec: raise RuntimeError("No inference_spec for the registered model.") + labels = kwargs.pop("labels", dict()) + if self.model_provider == ProviderAlibabaPAI: + default_labels = { + "Task": self.task, + "RootModelName": self.model_name, + "RootModelVersion": self.model_version, + "RootModelID": self.model_id, + "Domain": self.domain, + "CreatedBy": MODEL_TASK_CREATED_BY_QUICKSTART, + "BaseModelUri": self.uri, + } + default_labels.update(labels) + labels = default_labels + if is_local_run_instance_type(instance_type): return self._deploy_local( instance_type=instance_type, @@ -1740,6 +1758,7 @@ def deploy( options=options, wait=wait, serializer=serializer, + labels=labels, **kwargs, ) @@ -1906,7 +1925,7 @@ def get_estimator( if self.model_provider == ProviderAlibabaPAI: default_labels = { "BaseModelUri": self.uri, - "CreatedBy": "QuickStart", + "CreatedBy": MODEL_TASK_CREATED_BY_QUICKSTART, "Domain": self.domain, "RootModelID": self.model_id, "RootModelName": self.model_name, diff --git a/pai/predictor.py b/pai/predictor.py index f4f92e2..689c5e0 100644 --- a/pai/predictor.py +++ b/pai/predictor.py @@ -181,10 +181,19 @@ def service_status(self): return self._service_api_object["Status"] @property - def access_token(self): + def access_token(self) -> str: """Access token of the service.""" return self._service_api_object["AccessToken"] + @property + def labels(self) -> Dict[str, str]: + """Labels of the service.""" + labels = { + item["LabelKey"]: item["LabelValue"] + for item in self._service_api_object.get("Labels", []) + } + return labels + @property def console_uri(self): """Returns the console URI of the service.""" @@ -298,17 +307,14 @@ def delete_service(self): """Delete the service.""" self.session.service_api.delete(name=self.service_name) - def wait_for_ready(self, force: bool = False): + def wait_for_ready(self): """Wait until the service enter running status. - Args: - force (bool): Whether to force wait for ready. - Raises: RuntimeError: Raise if the service terminated unexpectedly. """ - if self.service_status == ServiceStatus.Running and not force: + if self.service_status == ServiceStatus.Running: return logger.info( @@ -327,6 +333,10 @@ def wait_for_ready(self, force: bool = False): self._wait_for_gateway_ready() self.refresh() + def wait(self): + """Wait for the service to be ready.""" + return self.wait_for_ready() + def _wait_for_gateway_ready(self, attempts: int = 60, interval: int = 2): """Hacky way to wait for the service gateway to be ready. @@ -337,6 +347,8 @@ def _wait_for_gateway_ready(self, attempts: int = 60, interval: int = 2): """ def _is_gateway_ready(): + # can't use HEAD method to check gateway status because the service will + # block the request until timeout. resp = self._send_request(method="GET") res = not ( # following status code and content indicates the gateway is not ready @@ -730,7 +742,8 @@ def openai(self, **kwargs) -> "OpenAI": raise ImportError( "openai package is not installed, install it with `pip install openai`." ) - base_url = kwargs.pop("base_url", self.endpoint + "/v1/") + + base_url = kwargs.pop("base_url", posixpath.join(self.endpoint + "v1/")) api_key = kwargs.pop("api_key", self.access_token) return OpenAI(base_url=base_url, api_key=api_key, **kwargs) @@ -1378,6 +1391,9 @@ def wait_for_ready(self): self._wait_local_server_ready() time.sleep(5) + def wait(self): + return self.wait_for_ready() + def _wait_local_server_ready( self, interval: int = 5, diff --git a/pyproject.toml b/pyproject.toml index 2f8fb69..a3e74c4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,7 +25,7 @@ src_paths = ["pai", "tests"] #known_first_party = ["pai", "tests"] [tool.pytest.ini_options] -timeout = 300 +timeout = 600 [doc8] max-line-length=88 diff --git a/tests/integration/test_model.py b/tests/integration/test_model.py index 1b038d0..e2dac19 100644 --- a/tests/integration/test_model.py +++ b/tests/integration/test_model.py @@ -305,8 +305,14 @@ def test_rm_deploy(self): ) p = m.deploy() - self.predictors.append(p) + + self.assertEqual(p.labels.get("RootModelID"), m.model_id) + self.assertEqual(p.labels.get("RootModelName"), m.model_name) + self.assertEqual(p.labels.get("RootModelVersion"), m.model_version) + self.assertEqual(p.labels.get("BaseModelUri"), m.uri) + self.assertEqual(p.labels.get("Task"), m.task) + self.assertEqual(p.labels.get("Domain"), m.domain) self.assertTrue(p.service_name) res = p.predict(["开心", "死亡"]) self.assertTrue(isinstance(res, list)) From 786057c72229bce23e6710206e2dfe6609c77e0e Mon Sep 17 00:00:00 2001 From: LiangQuan Date: Mon, 29 Apr 2024 13:41:09 +0800 Subject: [PATCH 24/59] release: 0.4.7 (#18) --- pai/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pai/version.py b/pai/version.py index 4f2f2ba..fac0359 100644 --- a/pai/version.py +++ b/pai/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -VERSION = "0.4.6.dev0" +VERSION = "0.4.7" From 094092d8e3ac380b35905da2571af3298526d5f2 Mon Sep 17 00:00:00 2001 From: LiangQuan Date: Mon, 20 May 2024 12:17:41 +0800 Subject: [PATCH 25/59] feat: add logging utils for the library. (#20) * update to development version * add logging utils * replace get_logger customized for pai module --- pai/api/base.py | 5 +- pai/api/client_factory.py | 5 +- pai/api/job.py | 4 +- pai/api/pipeline.py | 4 +- pai/api/service.py | 4 +- pai/api/tensorboard.py | 4 +- pai/common/docker_utils.py | 5 +- pai/common/git_utils.py | 5 +- pai/common/logging.py | 139 +++++++++++++++++++++++++++ pai/common/oss_utils.py | 5 +- pai/dataset.py | 4 +- pai/estimator.py | 4 +- pai/experiment.py | 4 +- pai/huggingface/estimator.py | 4 +- pai/huggingface/model.py | 4 +- pai/image.py | 4 +- pai/model.py | 6 +- pai/modelscope/estimator.py | 4 +- pai/modelscope/model.py | 4 +- pai/pipeline/component/_base.py | 14 +-- pai/pipeline/component/_container.py | 12 +-- pai/pipeline/core.py | 23 ++--- pai/pipeline/run.py | 12 +-- pai/predictor.py | 11 ++- pai/processor.py | 4 +- pai/serializers.py | 6 +- pai/session.py | 4 +- pai/toolkit/config.py | 4 +- pai/toolkit/helper/utils.py | 4 +- pai/version.py | 2 +- tests/unit/test_logging.py | 54 +++++++++++ tests/unit/utils.py | 6 ++ 32 files changed, 288 insertions(+), 86 deletions(-) create mode 100644 pai/common/logging.py create mode 100644 tests/unit/test_logging.py diff --git a/pai/api/base.py b/pai/api/base.py index bfb4d0b..05bccf2 100644 --- a/pai/api/base.py +++ b/pai/api/base.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import logging from abc import ABCMeta from typing import Any, Dict, List, Optional, Union @@ -22,7 +21,9 @@ from six import with_metaclass from Tea.model import TeaModel -logger = logging.getLogger(__name__) +from ..common.logging import get_logger + +logger = get_logger(__name__) class ServiceName(object): diff --git a/pai/api/client_factory.py b/pai/api/client_factory.py index 028efe5..35459d9 100644 --- a/pai/api/client_factory.py +++ b/pai/api/client_factory.py @@ -14,12 +14,11 @@ from __future__ import absolute_import -import logging - from alibabacloud_credentials.client import Client as CredentialClient from alibabacloud_sts20150401.client import Client as StsClient from alibabacloud_tea_openapi.models import Config +from ..common.logging import get_logger from ..common.utils import http_user_agent from ..libs.alibabacloud_aiworkspace20210204.client import Client as WorkspaceClient from ..libs.alibabacloud_eas20210701.client import Client as EasClient @@ -28,7 +27,7 @@ from ..libs.alibabacloud_paistudio20220112.client import Client as PaiClient from .base import ServiceName -_logger = logging.getLogger(__name__) +_logger = get_logger(__name__) DEFAULT_SERVICE_ENDPOINT_PATTERN = "{}.{}.aliyuncs.com" diff --git a/pai/api/job.py b/pai/api/job.py index 3212f7a..143f0f9 100644 --- a/pai/api/job.py +++ b/pai/api/job.py @@ -12,10 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -import logging from datetime import datetime from typing import Any, Dict, List +from ..common.logging import get_logger from ..libs.alibabacloud_pai_dlc20201203.models import ( GetJobEventsRequest, GetJobEventsResponseBody, @@ -27,7 +27,7 @@ ) from .base import PaginatedResult, ServiceName, WorkspaceScopedResourceAPI -logger = logging.getLogger(__name__) +logger = get_logger(__name__) class JobAPI(WorkspaceScopedResourceAPI): diff --git a/pai/api/pipeline.py b/pai/api/pipeline.py index 2da666b..546f39e 100644 --- a/pai/api/pipeline.py +++ b/pai/api/pipeline.py @@ -12,9 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -import logging from typing import Any, Dict +from ..common.logging import get_logger from ..libs.alibabacloud_paiflow20210202.models import ( CreatePipelineRequest, CreatePipelineResponseBody, @@ -26,7 +26,7 @@ ) from .base import PaginatedResult, ServiceName, WorkspaceScopedResourceAPI -logger = logging.getLogger(__name__) +logger = get_logger(__name__) class PipelineAPI(WorkspaceScopedResourceAPI): diff --git a/pai/api/service.py b/pai/api/service.py index 331df2c..da95be6 100644 --- a/pai/api/service.py +++ b/pai/api/service.py @@ -13,10 +13,10 @@ # limitations under the License. import json -import logging import typing from typing import Any, Dict, Union +from ..common.logging import get_logger from ..libs.alibabacloud_eas20210701.models import ( CreateServiceRequest, CreateServiceResponseBody, @@ -30,7 +30,7 @@ ) from .base import PaginatedResult, ResourceAPI, ServiceName -logger = logging.getLogger(__name__) +logger = get_logger(__name__) class ServiceAPI(ResourceAPI): diff --git a/pai/api/tensorboard.py b/pai/api/tensorboard.py index d1bccf0..6dd7424 100644 --- a/pai/api/tensorboard.py +++ b/pai/api/tensorboard.py @@ -12,9 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -import logging from typing import Optional +from ..common.logging import get_logger from ..libs.alibabacloud_pai_dlc20201203.models import ( CreateTensorboardRequest, CreateTensorboardResponseBody, @@ -27,7 +27,7 @@ ) from .base import PaginatedResult, ServiceName, WorkspaceScopedResourceAPI -logger = logging.getLogger(__name__) +logger = get_logger(__name__) class TensorBoardAPI(WorkspaceScopedResourceAPI): diff --git a/pai/common/docker_utils.py b/pai/common/docker_utils.py index 6f6507d..d584740 100644 --- a/pai/common/docker_utils.py +++ b/pai/common/docker_utils.py @@ -13,13 +13,14 @@ # limitations under the License. import io -import logging import subprocess import time from random import randint from typing import Any, Dict, List, Optional, Union -logger = logging.getLogger(__name__) +from .logging import get_logger + +logger = get_logger(__name__) def _run_command(command: List[str], input: Optional[str] = None): diff --git a/pai/common/git_utils.py b/pai/common/git_utils.py index 991fa5a..b41983f 100644 --- a/pai/common/git_utils.py +++ b/pai/common/git_utils.py @@ -14,7 +14,6 @@ from __future__ import absolute_import -import logging import os import subprocess import tempfile @@ -24,7 +23,9 @@ import six from six.moves import urllib -logger = logging.getLogger(__name__) +from .logging import get_logger + +logger = get_logger(__name__) def git_clone_repo(git_config: Dict[str, str], source_dir: Optional[str] = None): diff --git a/pai/common/logging.py b/pai/common/logging.py new file mode 100644 index 0000000..6799ac9 --- /dev/null +++ b/pai/common/logging.py @@ -0,0 +1,139 @@ +# Copyright 2023 Alibaba, Inc. or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging +import os +import threading +from typing import Optional + +PAI_LOG_LEVEL = "PAI_LOG_LEVEL" + + +_lock = threading.Lock() +_default_handler: Optional[logging.Handler] = None + +_LOG_LEVEL_MAPPING = { + "debug": logging.DEBUG, + "info": logging.INFO, + "warning": logging.WARNING, + "error": logging.ERROR, + "critical": logging.CRITICAL, +} + +_default_log_level = logging.WARNING + + +def _get_default_logging_level(): + """Get the default logging level for the pai library.""" + level_str = os.getenv(PAI_LOG_LEVEL, None) + if level_str: + if level_str.lower() in _LOG_LEVEL_MAPPING: + return _LOG_LEVEL_MAPPING[level_str.lower()] + else: + logging.getLogger().warning( + f"Unknown option PAI_LOG_LEVEL={level_str}, " + f"has to be one of: { ', '.join(_LOG_LEVEL_MAPPING.keys()) }" + ) + return _default_log_level + + +def _get_library_name() -> str: + return __name__.split(".")[0] + + +def _get_library_root_logger() -> logging.Logger: + return logging.getLogger(_get_library_name()) + + +def _configure_library_root_logger() -> None: + global _default_handler + + with _lock: + if _default_handler: + return + handler = logging.StreamHandler() + library_root_logger = _get_library_root_logger() + library_root_logger.addHandler(handler) + library_root_logger.setLevel(_get_default_logging_level()) + library_root_logger.propagate = False + _default_handler = handler + + +def _reset_library_root_logger() -> None: + global _default_handler + + with _lock: + if not _default_handler: + return + library_root_logger = _get_library_root_logger() + library_root_logger.removeHandler(_default_handler) + library_root_logger.setLevel(logging.NOTSET) + _default_handler = None + + +def get_log_levels_dict(): + return _LOG_LEVEL_MAPPING + + +def get_logger(name: Optional[str] = None) -> logging.Logger: + """ + Return a logger with the specified module name. + """ + + if name is None: + name = _get_library_name() + + _configure_library_root_logger() + return logging.getLogger(name) + + +def get_log_level() -> int: + """ + Return the current level for the "pai" module. + + Returns: + `int`: The logging level. + """ + + _configure_library_root_logger() + return _get_library_root_logger().getEffectiveLevel() + + +def set_log_level(verbosity: int) -> None: + """ + Set the log level for the pai root logger. + """ + + _configure_library_root_logger() + _get_library_root_logger().setLevel(verbosity) + + +def set_log_level_info(): + """Set the log level to the `INFO` level.""" + return set_log_level(logging.INFO) + + +def set_log_level_warning(): + """Set the log level to the `WARNING` level.""" + return set_log_level(logging.WARNING) + + +def set_log_level_debug(): + """Set the log level to the `DEBUG` level.""" + return set_log_level(logging.DEBUG) + + +def set_log_level_error(): + """Set the log level to the `ERROR` level.""" + return set_log_level(logging.ERROR) diff --git a/pai/common/oss_utils.py b/pai/common/oss_utils.py index 23a5066..5bf2efa 100644 --- a/pai/common/oss_utils.py +++ b/pai/common/oss_utils.py @@ -15,7 +15,6 @@ from __future__ import absolute_import import glob -import logging import os.path import pathlib import tarfile @@ -29,7 +28,9 @@ from oss2.credentials import Credentials, CredentialsProvider from tqdm.autonotebook import tqdm -logger = logging.getLogger(__name__) +from .logging import get_logger + +logger = get_logger(__name__) class _ProgressCallbackTqdm(tqdm): diff --git a/pai/dataset.py b/pai/dataset.py index 1c33427..236d202 100644 --- a/pai/dataset.py +++ b/pai/dataset.py @@ -12,14 +12,14 @@ # See the License for the specific language governing permissions and # limitations under the License. -import logging from typing import Any, Dict, List, Optional from .common import ProviderAlibabaPAI +from .common.logging import get_logger from .common.utils import make_list_resource_iterator from .session import Session, get_default_session -logger = logging.getLogger(__name__) +logger = get_logger(__name__) def list_common_datasets( diff --git a/pai/estimator.py b/pai/estimator.py index 55887e8..1b78e98 100644 --- a/pai/estimator.py +++ b/pai/estimator.py @@ -14,7 +14,6 @@ import distutils.dir_util import json -import logging import os import posixpath import re @@ -37,6 +36,7 @@ from .common.configs import UserVpcConfig from .common.consts import INSTANCE_TYPE_LOCAL_GPU, FileSystemInputScheme, JobType from .common.docker_utils import ContainerRun, run_container +from .common.logging import get_logger from .common.oss_utils import OssUriObj, download, is_oss_uri, upload from .common.utils import ( is_filesystem_uri, @@ -55,7 +55,7 @@ from .serializers import SerializerBase from .session import Session, get_default_session -logger = logging.getLogger(__name__) +logger = get_logger(__name__) DEFAULT_OUTPUT_MODEL_CHANNEL_NAME = "model" DEFAULT_CHECKPOINT_CHANNEL_NAME = "checkpoints" diff --git a/pai/experiment.py b/pai/experiment.py index 401d714..1087342 100644 --- a/pai/experiment.py +++ b/pai/experiment.py @@ -12,14 +12,14 @@ # See the License for the specific language governing permissions and # limitations under the License. -import logging import webbrowser from typing import Iterator, Optional +from .common.logging import get_logger from .session import Session, get_default_session from .tensorboard import TensorBoard -logger = logging.getLogger(__name__) +logger = get_logger(__name__) _default_session = None diff --git a/pai/huggingface/estimator.py b/pai/huggingface/estimator.py index 99ffbc2..b08c364 100644 --- a/pai/huggingface/estimator.py +++ b/pai/huggingface/estimator.py @@ -12,15 +12,15 @@ # See the License for the specific language governing permissions and # limitations under the License. -import logging from typing import Any, Dict, List, Optional from ..api.image import ImageLabel +from ..common.logging import get_logger from ..common.utils import to_semantic_version from ..estimator import Estimator from ..session import Session -logger = logging.getLogger(__name__) +logger = get_logger(__name__) class HuggingFaceEstimator(Estimator): diff --git a/pai/huggingface/model.py b/pai/huggingface/model.py index ca85178..63be221 100644 --- a/pai/huggingface/model.py +++ b/pai/huggingface/model.py @@ -12,9 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -import logging from typing import Any, Dict, List, Optional, Union +from ..common.logging import get_logger from ..common.utils import to_semantic_version from ..image import ImageLabel from ..model import ( @@ -26,7 +26,7 @@ from ..serializers import SerializerBase from ..session import Session, get_default_session -logger = logging.getLogger(__name__) +logger = get_logger(__name__) class HuggingFaceModel(ModelBase): diff --git a/pai/image.py b/pai/image.py index 21de961..6644b03 100644 --- a/pai/image.py +++ b/pai/image.py @@ -12,15 +12,15 @@ # See the License for the specific language governing permissions and # limitations under the License. -import logging import re from typing import Any, Dict, List, Optional from .api.image import SUPPORTED_IMAGE_FRAMEWORKS, ImageLabel +from .common.logging import get_logger from .common.utils import make_list_resource_iterator, to_semantic_version from .session import Session, get_default_session -logger = logging.getLogger(__name__) +logger = get_logger(__name__) _NORMALIZED_FRAMEWORK_NAMES = { diff --git a/pai/model.py b/pai/model.py index 2ee1e08..4e5ebb9 100644 --- a/pai/model.py +++ b/pai/model.py @@ -15,7 +15,6 @@ import copy import distutils.dir_util import json -import logging import os.path import posixpath import shlex @@ -34,6 +33,7 @@ from .common.configs import UserVpcConfig from .common.consts import INSTANCE_TYPE_LOCAL_GPU, ModelFormat from .common.docker_utils import ContainerRun, run_container +from .common.logging import get_logger from .common.oss_utils import OssUriObj, download, is_oss_uri, upload from .common.utils import ( generate_repr, @@ -50,7 +50,7 @@ if typing.TYPE_CHECKING: from pai.estimator import AlgorithmEstimator -logger = logging.getLogger(__name__) +logger = get_logger(__name__) # Reserved ports for internal use, do not use them for service _RESERVED_PORTS = [8080, 9090] @@ -1118,7 +1118,7 @@ def _wait_local_server_ready( break except requests.ConnectionError: # ConnectionError means server is not ready. - logging.debug("Waiting for the container to be ready...") + logger.debug("Waiting for the container to be ready...") time.sleep(interval) continue diff --git a/pai/modelscope/estimator.py b/pai/modelscope/estimator.py index 43ce15d..ffa2b96 100644 --- a/pai/modelscope/estimator.py +++ b/pai/modelscope/estimator.py @@ -12,15 +12,15 @@ # See the License for the specific language governing permissions and # limitations under the License. -import logging from typing import Any, Dict, List, Optional from ..api.image import ImageLabel +from ..common.logging import get_logger from ..common.utils import to_semantic_version from ..estimator import Estimator from ..session import Session -logger = logging.getLogger(__name__) +logger = get_logger(__name__) class ModelScopeEstimator(Estimator): diff --git a/pai/modelscope/model.py b/pai/modelscope/model.py index 2246d4e..d844fca 100644 --- a/pai/modelscope/model.py +++ b/pai/modelscope/model.py @@ -12,10 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -import logging from typing import Any, Dict, List, Optional, Union from ..api.image import ImageLabel +from ..common.logging import get_logger from ..common.utils import to_semantic_version from ..model import ( DefaultServiceConfig, @@ -26,7 +26,7 @@ from ..serializers import SerializerBase from ..session import Session, get_default_session -logger = logging.getLogger(__name__) +logger = get_logger(__name__) class ModelScopeModel(ModelBase): diff --git a/pai/pipeline/component/_base.py b/pai/pipeline/component/_base.py index ad2668c..599206c 100644 --- a/pai/pipeline/component/_base.py +++ b/pai/pipeline/component/_base.py @@ -13,19 +13,19 @@ # limitations under the License. import itertools -import logging import uuid from abc import ABCMeta, abstractmethod import six -from pai.common.utils import random_str -from pai.common.yaml_utils import dump as yaml_dump -from pai.common.yaml_utils import dump_all as yaml_dump_all -from pai.pipeline.types import IO_TYPE_INPUTS, IO_TYPE_OUTPUTS, InputsSpec, OutputsSpec -from pai.session import get_default_session +from ...common.logging import get_logger +from ...common.utils import random_str +from ...common.yaml_utils import dump as yaml_dump +from ...common.yaml_utils import dump_all as yaml_dump_all +from ...session import get_default_session +from ..types import IO_TYPE_INPUTS, IO_TYPE_OUTPUTS, InputsSpec, OutputsSpec -logger = logging.getLogger(__name__) +logger = get_logger(__name__) DEFAULT_PIPELINE_API_VERSION = "core/v1" diff --git a/pai/pipeline/component/_container.py b/pai/pipeline/component/_container.py index bf196b5..491e545 100644 --- a/pai/pipeline/component/_container.py +++ b/pai/pipeline/component/_container.py @@ -16,22 +16,22 @@ from __future__ import print_function -import logging import uuid import six from deprecated import deprecated -from pai.common.yaml_utils import dump as yaml_dump -from pai.pipeline.component._base import UnRegisteredComponent -from pai.pipeline.types.variable import PipelineVariable -from pai.session import get_default_session +from ...common.logging import get_logger +from ...common.yaml_utils import dump as yaml_dump +from ...session import get_default_session +from ..types.variable import PipelineVariable +from ._base import UnRegisteredComponent PAI_MANIFEST_SPEC_INPUTS_ENV_KEY = "PAI_MANIFEST_SPEC_INPUTS" PAI_MANIFEST_SPEC_OUTPUTS_ENV_KEY = "PAI_MANIFEST_SPEC_OUTPUTS" PAI_INPUTS_PARAMETERS_ENV_KEY = "PAI_INPUTS_PARAMETERS" -_logger = logging.getLogger(__name__) +_logger = get_logger(__name__) @deprecated( diff --git a/pai/pipeline/core.py b/pai/pipeline/core.py index 653eebf..87932e6 100644 --- a/pai/pipeline/core.py +++ b/pai/pipeline/core.py @@ -14,22 +14,17 @@ from __future__ import absolute_import -import logging from collections import Counter, defaultdict -from pai.common.yaml_utils import dump as yaml_dump -from pai.common.yaml_utils import dump_all as yaml_dump_all -from pai.pipeline.component._base import UnRegisteredComponent -from pai.pipeline.types import ( - InputsSpec, - OutputsSpec, - PipelineParameter, - PipelineVariable, -) -from pai.pipeline.types.artifact import PipelineArtifact, PipelineArtifactElement -from pai.session import get_default_session - -logger = logging.getLogger(__name__) +from ..common.logging import get_logger +from ..common.yaml_utils import dump as yaml_dump +from ..common.yaml_utils import dump_all as yaml_dump_all +from ..session import get_default_session +from .component._base import UnRegisteredComponent +from .types import InputsSpec, OutputsSpec, PipelineParameter, PipelineVariable +from .types.artifact import PipelineArtifact, PipelineArtifactElement + +logger = get_logger(__name__) class Pipeline(UnRegisteredComponent): diff --git a/pai/pipeline/run.py b/pai/pipeline/run.py index c286a23..466d5a6 100644 --- a/pai/pipeline/run.py +++ b/pai/pipeline/run.py @@ -14,18 +14,18 @@ from __future__ import absolute_import -import logging import time from concurrent.futures import ThreadPoolExecutor from datetime import datetime from typing import Callable, Optional -from pai.api.base import PaginatedResult -from pai.exception import PAIException -from pai.pipeline.artifact import ArchivedArtifact -from pai.session import Session, get_default_session +from ..api.base import PaginatedResult +from ..common.logging import get_logger +from ..exception import PAIException +from ..session import Session, get_default_session +from .artifact import ArchivedArtifact -logger = logging.getLogger(__name__) +logger = get_logger(__name__) # TODO: review the status names of the PipelineRun. diff --git a/pai/predictor.py b/pai/predictor.py index 689c5e0..d7a78e2 100644 --- a/pai/predictor.py +++ b/pai/predictor.py @@ -16,7 +16,6 @@ import base64 import functools import json -import logging import posixpath import time from abc import ABC, abstractmethod @@ -30,6 +29,7 @@ from .common.consts import FrameworkTypes from .common.docker_utils import ContainerRun +from .common.logging import get_logger from .common.utils import http_user_agent, is_package_available from .exception import PredictionException from .serializers import ( @@ -44,7 +44,7 @@ from openai import OpenAI -logger = logging.getLogger(__name__) +logger = get_logger(__name__) _PAI_SERVICE_CONSOLE_URI_PATTERN = ( "https://pai.console.aliyun.com/?regionId={region_id}#" @@ -350,6 +350,11 @@ def _is_gateway_ready(): # can't use HEAD method to check gateway status because the service will # block the request until timeout. resp = self._send_request(method="GET") + logger.debug( + "Check gateway status result: status_code=%s content=%s", + resp.status_code, + resp.content, + ) res = not ( # following status code and content indicates the gateway is not ready ( @@ -1417,6 +1422,6 @@ def _wait_local_server_ready( break except requests.ConnectionError: # ConnectionError means server is not ready. - logging.debug("Waiting for the container to be ready...") + logger.debug("Waiting for the container to be ready...") time.sleep(interval) continue diff --git a/pai/processor.py b/pai/processor.py index 0af0259..a0e2bf5 100644 --- a/pai/processor.py +++ b/pai/processor.py @@ -11,7 +11,6 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -import logging import os import posixpath import time @@ -20,6 +19,7 @@ from .common.configs import UserVpcConfig from .common.consts import JobType, StoragePathCategory +from .common.logging import get_logger from .common.oss_utils import OssUriObj, is_oss_uri, upload from .common.utils import ( experimental, @@ -34,7 +34,7 @@ from .experiment import ExperimentConfig from .session import Session, get_default_session -logger = logging.getLogger(__name__) +logger = get_logger(__name__) def build_code_input( diff --git a/pai/serializers.py b/pai/serializers.py index d74c926..432f796 100644 --- a/pai/serializers.py +++ b/pai/serializers.py @@ -13,7 +13,6 @@ # limitations under the License. import json -import logging import urllib.request from abc import ABC, abstractmethod from typing import Any, Dict, List, Optional, Tuple, Union @@ -26,9 +25,10 @@ from eas_prediction import pytorch_predict_pb2 as pt_pb from eas_prediction import tf_request_pb2 as tf_pb -from pai.session import Session, get_default_session +from .common.logging import get_logger +from .session import Session, get_default_session -logger = logging.getLogger(__name__) +logger = get_logger(__name__) def _is_pil_image(data) -> bool: diff --git a/pai/session.py b/pai/session.py index bb90a4d..be001cd 100644 --- a/pai/session.py +++ b/pai/session.py @@ -15,7 +15,6 @@ from __future__ import absolute_import import json -import logging import os.path import posixpath from datetime import datetime @@ -27,10 +26,11 @@ from .api.api_container import ResourceAPIsContainerMixin from .common.consts import DEFAULT_CONFIG_PATH +from .common.logging import get_logger from .common.oss_utils import CredentialProviderWrapper, OssUriObj from .common.utils import is_domain_connectable, make_list_resource_iterator -logger = logging.getLogger(__name__) +logger = get_logger(__name__) # Environment variable that indicates where the config path is located. # If it is not provided, "$HOME/.pai/config.json" is used as the default config path. diff --git a/pai/toolkit/config.py b/pai/toolkit/config.py index b50eb25..eb0893e 100644 --- a/pai/toolkit/config.py +++ b/pai/toolkit/config.py @@ -13,7 +13,6 @@ # limitations under the License. import locale -import logging import os.path from enum import Enum from typing import Any, Dict, List, Optional @@ -35,6 +34,7 @@ from prompt_toolkit import prompt from prompt_toolkit.validation import Validator +from ..common.logging import get_logger from ..common.oss_utils import OssUriObj from ..common.utils import ( is_domain_connectable, @@ -57,7 +57,7 @@ validate_bucket_name, ) -logger = logging.getLogger(__name__) +logger = get_logger(__name__) DEFAULT_CONFIG_PATH = os.path.join(os.path.expanduser("~"), ".pai", "config.json") DEFAULT_CREDENTIAL_INI_PATH = os.path.join( diff --git a/pai/toolkit/helper/utils.py b/pai/toolkit/helper/utils.py index 881b54f..448d680 100644 --- a/pai/toolkit/helper/utils.py +++ b/pai/toolkit/helper/utils.py @@ -13,7 +13,6 @@ # limitations under the License. import locale -import logging import os import re from typing import List @@ -37,10 +36,11 @@ from ...api.base import ServiceName from ...api.client_factory import ClientFactory from ...api.workspace import WorkspaceAPI, WorkspaceConfigKeys +from ...common.logging import get_logger from ...common.oss_utils import CredentialProviderWrapper, OssUriObj from ...common.utils import make_list_resource_iterator -logger = logging.getLogger(__name__) +logger = get_logger(__name__) locale_code, _ = locale.getdefaultlocale() diff --git a/pai/version.py b/pai/version.py index fac0359..7b413b3 100644 --- a/pai/version.py +++ b/pai/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -VERSION = "0.4.7" +VERSION = "0.4.8.dev0" diff --git a/tests/unit/test_logging.py b/tests/unit/test_logging.py new file mode 100644 index 0000000..5875015 --- /dev/null +++ b/tests/unit/test_logging.py @@ -0,0 +1,54 @@ +# Copyright 2023 Alibaba, Inc. or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import logging + +from pai.common.logging import ( + _reset_library_root_logger, + get_log_level, + get_logger, + set_log_level_debug, + set_log_level_info, +) + +from .utils import mock_env + + +@mock_env(PAI_LOG_LEVEL="DEBUG") +def test_log_level(): + _reset_library_root_logger() + + assert get_log_level() == logging.DEBUG + + +@mock_env(PAI_LOG_LEVEL="INFO") +def test_get_logger(): + _reset_library_root_logger() + + lib_root_logger = get_logger() + logger = get_logger("pai.abc") + + assert logger.parent == lib_root_logger + assert lib_root_logger.getEffectiveLevel() == logging.INFO + + +def test_set_log_level(): + _reset_library_root_logger() + + assert get_log_level() == logging.WARNING + + set_log_level_info() + assert get_log_level() == logging.INFO + + set_log_level_debug() + assert get_log_level() == logging.DEBUG diff --git a/tests/unit/utils.py b/tests/unit/utils.py index d5bfff4..3aa16ec 100644 --- a/tests/unit/utils.py +++ b/tests/unit/utils.py @@ -18,6 +18,7 @@ import os import re +import mock import six import yaml @@ -150,3 +151,8 @@ def file_checksum(file_name, hash_type="md5"): for chunk in iter(lambda: f.read(256 * 1024), b""): hash_md5.update(chunk) return hash_md5.hexdigest() + + +def mock_env(**kwargs): + """Decorator to set environment variables for a test function.""" + return mock.patch.dict(os.environ, kwargs) From 7f3c8d765e879d41761c92fca8a16ef1ae335923 Mon Sep 17 00:00:00 2001 From: LiangQuan Date: Mon, 20 May 2024 14:44:43 +0800 Subject: [PATCH 26/59] fix session setup and command line utilities for configuration. (#21) * fix: fix release_trigger github action * fix tensorboard initialize * fix setup_default_session * fix toolkit used for config initialization * fix service console url pattern --- .github/workflows/release_trigger.yaml | 8 +++ pai/common/oss_utils.py | 2 +- pai/predictor.py | 3 +- pai/session.py | 12 ++-- pai/tensorboard.py | 2 +- pai/toolkit/config.py | 76 ++++++++++++++++++++------ pai/toolkit/helper/utils.py | 36 ++++++------ 7 files changed, 95 insertions(+), 44 deletions(-) diff --git a/.github/workflows/release_trigger.yaml b/.github/workflows/release_trigger.yaml index f3a367a..cf45247 100644 --- a/.github/workflows/release_trigger.yaml +++ b/.github/workflows/release_trigger.yaml @@ -18,6 +18,7 @@ jobs: if: github.event.pull_request.merged == true && startsWith(github.head_ref, 'releases/v') env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} steps: - uses: actions/checkout@v4 - name: Set up Python 3.8 @@ -39,3 +40,10 @@ jobs: VERSION=$(python -c "from pai.version import VERSION; print(VERSION)") git tag v$VERSION git push origin v$VERSION +# git tag pushed by GitHub action bot will not trigger another action. + - name: Install dependencies + run: pip install wheel setuptools twine + - name: Build package + run: python setup.py sdist bdist_wheel + - name: Publish package to PyPI + run: twine upload dist/* --skip-existing -u __token__ -p $PYPI_TOKEN diff --git a/pai/common/oss_utils.py b/pai/common/oss_utils.py index 5bf2efa..908b8f5 100644 --- a/pai/common/oss_utils.py +++ b/pai/common/oss_utils.py @@ -450,7 +450,7 @@ def download( class CredentialProviderWrapper(CredentialsProvider): """A wrapper class for the credential provider of OSS.""" - def __init__(self, config: CredentialConfig): + def __init__(self, config: Union[CredentialConfig] = None): self.client = CredentialClient(config) def get_credentials(self) -> Credentials: diff --git a/pai/predictor.py b/pai/predictor.py index d7a78e2..fced989 100644 --- a/pai/predictor.py +++ b/pai/predictor.py @@ -47,7 +47,7 @@ logger = get_logger(__name__) _PAI_SERVICE_CONSOLE_URI_PATTERN = ( - "https://pai.console.aliyun.com/?regionId={region_id}#" + "https://pai.console.aliyun.com/?regionId={region_id}&workspaceId={workspace_id}#" "/eas/serviceDetail/{service_name}/detail" ) @@ -198,6 +198,7 @@ def labels(self) -> Dict[str, str]: def console_uri(self): """Returns the console URI of the service.""" return _PAI_SERVICE_CONSOLE_URI_PATTERN.format( + workspace_id=self.session.workspace_id, region_id=self.session.region_id, service_name=self.service_name, ) diff --git a/pai/session.py b/pai/session.py index be001cd..add6a07 100644 --- a/pai/session.py +++ b/pai/session.py @@ -108,12 +108,12 @@ def setup_default_session( # override the config from default session default_session = get_default_session() - - region_id = region_id or default_session.region_id - workspace_id = workspace_id or default_session.workspace_id - oss_bucket_name = oss_bucket_name or default_session.oss_bucket_name - oss_endpoint = oss_endpoint or default_session.oss_endpoint - credential_config = credential_config or default_session.credential_config + if default_session: + region_id = region_id or default_session.region_id + workspace_id = workspace_id or default_session.workspace_id + oss_bucket_name = oss_bucket_name or default_session.oss_bucket_name + oss_endpoint = oss_endpoint or default_session.oss_endpoint + credential_config = credential_config or default_session.credential_config session = Session( region_id=region_id, diff --git a/pai/tensorboard.py b/pai/tensorboard.py index 483a78b..2d32c5c 100644 --- a/pai/tensorboard.py +++ b/pai/tensorboard.py @@ -52,7 +52,7 @@ class TensorBoard(object): def __init__(self, tensorboard_id: str, session: Optional[Session] = None): self.session = session or get_default_session() self.tensorboard_id = tensorboard_id - self._api_object = session.tensorboard_api.get(tensorboard_id) + self._api_object = self.session.tensorboard_api.get(tensorboard_id) def __repr__(self): return "TensorBoard(tensorboard_id={}, name={}, status={})".format( diff --git a/pai/toolkit/config.py b/pai/toolkit/config.py index eb0893e..ca1b79d 100644 --- a/pai/toolkit/config.py +++ b/pai/toolkit/config.py @@ -22,6 +22,7 @@ from alibabacloud_credentials.exceptions import CredentialException from alibabacloud_credentials.models import Config as CredentialConfig from alibabacloud_credentials.providers import ( + CredentialsUriProvider, EcsRamRoleCredentialProvider, EnvironmentVariableCredentialsProvider, OIDCRoleArnCredentialProvider, @@ -97,6 +98,7 @@ class CredentialProviderType(Enum): RamRoleArn = RamRoleArnCredentialProvider RsaKeyPair = RsaKeyPairCredentialProvider Profile = ProfileCredentialsProvider + CredentialUri = CredentialsUriProvider @classmethod def get_current_provider(cls) -> Optional["CredentialProviderType"]: @@ -108,7 +110,8 @@ def get_current_provider(cls) -> Optional["CredentialProviderType"]: if p.get_credentials(): return d.get(p.__class__) - def credential_hint(self) -> str: + @classmethod + def credential_hint(cls, cred_type: Optional["CredentialProviderType"]) -> str: provider_hints = { CredentialProviderType.EnvironmentVariable: localized_text( "The credential source is: Environment Variable", @@ -134,10 +137,14 @@ def credential_hint(self) -> str: "The credential source is: Profile", "凭证来源: Profile(~/.alibabacloud/credentials.ini)", ), + CredentialProviderType.CredentialUri: localized_text( + "The credential source is: CredentialUri (EnvironmentVairbale ALIBABA_CLOUD_CREDENTIALS_URI)", + "凭证来源: CredentialUri (环境变量 ALIBABA_CLOUD_CREDENTIALS_URI)", + ), } return provider_hints.get( - self, + cred_type, localized_text( "The credential source is: Unknown", "凭证来源: 未知", @@ -185,14 +192,17 @@ def prompt_for_credential(): ) credential_client = CredentialClient(config=credential_config) else: + # Credential chain documentation: + # https://help.aliyun.com/zh/sdk/developer-reference/v2-manage-python-access-credentials print( localized_text( "Use credential from default credential provider chain:", "使用默认的凭证链获取访问密钥:", ) ) - credential_source_hint = ( - CredentialProviderType.get_current_provider().credential_hint() + + credential_source_hint = CredentialProviderType.credential_hint( + CredentialProviderType.get_current_provider() ) print(credential_source_hint) credential_client = default_credential_client @@ -243,6 +253,12 @@ def prompt_for_region(): for key in REGION_ID_ENV_KEYS: region_id = os.environ.get(key) if region_id: + print( + localized_text( + f"Config RegionId from environment variable({key}): {region_id} ", + f"从环境变量({key})中获取RegionId: {region_id}", + ) + ) return region_id region_name_map = {r["regionId"]: r["regionName"] for r in REGION_INFOS} @@ -416,7 +432,6 @@ def prompt_for_oss_bucket(user_profile: UserProfile, workspace_id: str): ) bucket_name = prompt_for_create_oss_bucket(user_profile, workspace_id) else: - buckets: List[SimplifiedBucketInfo] = user_profile.list_oss_buckets() index = radio_list_prompt( localized_text( "Please select the OSS Bucket you want to use:", @@ -427,24 +442,48 @@ def prompt_for_oss_bucket(user_profile: UserProfile, workspace_id: str): ) bucket_name = buckets[index].name - bucket_info = user_profile.get_bucket_info(bucket_name) + try: + bucket_info = user_profile.get_bucket_info(bucket_name=bucket_name) + except oss2.exceptions.AccessDenied: + # try to get bucket info with ListBuckets API if the user has no permission to + # GetBucketInfo API. + buckets = user_profile.list_oss_buckets(prefix=bucket_name) + bucket_info = next((b for b in buckets if b.name == bucket_name), None) + + if not bucket_info: + print_warning( + localized_text( + "Failed to get bucket info, use default endpoint.", + "获取 Bucket 信息失败,使用默认 Endpoint。", + ) + ) + region_id = user_profile.region_id + extranet_endpoint, intranet_endpoint = ( + f"oss-{region_id}.aliyuncs.com", + f"oss-{region_id}-internal.aliyuncs.com", + ) + else: + extranet_endpoint, intranet_endpoint = ( + bucket_info.extranet_endpoint, + bucket_info.intranet_endpoint, + ) # If Workspace has no default OSS storage URI and user has permission to edit, # prompt to set the default OSS storage URI. if not default_storage_uri and user_profile.has_permission_edit_config( workspace_id=workspace_id ): - prompt_for_set_default_oss_storage(user_profile, workspace_id, bucket_info) + prompt_for_set_default_oss_storage( + user_profile, workspace_id, bucket_name, intranet_endpoint=intranet_endpoint + ) row_format = "{:<60}{}" - intra_endpoint_connectable = is_domain_connectable( - bucket_info.intranet_endpoint, timeout=1 - ) + intra_endpoint_connectable = is_domain_connectable(intranet_endpoint, timeout=1) candidates = [ ( - bucket_info.intranet_endpoint, + intranet_endpoint, row_format.format( - bucket_info.intranet_endpoint, + intranet_endpoint, localized_text( "Internal endpoint (Please use in PAI-DSW Notebook, ECS and other " "intranet environment)", @@ -453,9 +492,9 @@ def prompt_for_oss_bucket(user_profile: UserProfile, workspace_id: str): ), ), ( - bucket_info.extranet_endpoint, + extranet_endpoint, row_format.format( - bucket_info.extranet_endpoint, + extranet_endpoint, localized_text( "Public endpoint", "外网Endpoint", @@ -479,7 +518,10 @@ def prompt_for_oss_bucket(user_profile: UserProfile, workspace_id: str): def prompt_for_set_default_oss_storage( - user_profile: UserProfile, workspace_id: str, bucket_info + user_profile: UserProfile, + workspace_id: str, + bucket_name: str, + intranet_endpoint: str, ): yes_no = confirm( localized_text( @@ -488,7 +530,9 @@ def prompt_for_set_default_oss_storage( ) ) if yes_no: - user_profile.set_default_oss_storage(workspace_id, bucket_info) + user_profile.set_default_oss_storage( + workspace_id, bucket_name, intranet_endpoint=intranet_endpoint + ) def prompt_for_create_oss_bucket(user_profile: UserProfile, workspace_id): diff --git a/pai/toolkit/helper/utils.py b/pai/toolkit/helper/utils.py index 448d680..474fc0d 100644 --- a/pai/toolkit/helper/utils.py +++ b/pai/toolkit/helper/utils.py @@ -25,7 +25,7 @@ GetCallerIdentityResponseBody as CallerIdentity, ) from alibabacloud_tea_openapi import models as open_api_models -from oss2.models import SimplifiedBucketInfo +from oss2.models import BucketInfo, SimplifiedBucketInfo from prompt_toolkit import Application from prompt_toolkit.key_binding import KeyBindings, merge_key_bindings from prompt_toolkit.key_binding.defaults import load_key_bindings @@ -153,7 +153,7 @@ def identify_type(self): def get_default_oss_endpoint(self): return "https://oss-{}.aliyuncs.com".format(self.region_id) - def list_oss_buckets(self): + def list_oss_buckets(self, prefix: str = "") -> List[SimplifiedBucketInfo]: buckets: List[SimplifiedBucketInfo] = [] service = oss2.Service( auth=oss2.ProviderAuth( @@ -166,7 +166,9 @@ def list_oss_buckets(self): marker = "" while True: - res: oss2.models.ListBucketsResult = service.list_buckets(marker=marker) + res: oss2.models.ListBucketsResult = service.list_buckets( + prefix=prefix, marker=marker + ) buckets.extend( [b for b in res.buckets if self.region_id in b.location] or [] ) @@ -177,22 +179,16 @@ def list_oss_buckets(self): return buckets - def get_bucket_info(self, bucket_name): - service = oss2.Service( - auth=oss2.ProviderAuth( - credentials_provider=CredentialProviderWrapper( - config=self.credential_config, - ), + def get_bucket_info(self, bucket_name) -> BucketInfo: + auth = oss2.ProviderAuth( + credentials_provider=CredentialProviderWrapper( + config=self.credential_config, ), - endpoint=self.get_default_oss_endpoint(), ) - res: oss2.models.ListBucketsResult = service.list_buckets(prefix=bucket_name) - bucket_info = next((b for b in res.buckets if b.name == bucket_name), None) - if not bucket_info: - raise ValueError( - f"Not found bucket with the specific name: bucket_name={bucket_name}" - ) - + bucket = oss2.Bucket( + auth, self.get_default_oss_endpoint(), bucket_name=bucket_name + ) + bucket_info = bucket.get_bucket_info() return bucket_info def create_oss_bucket(self, bucket_name): @@ -246,9 +242,11 @@ def get_default_oss_storage_uri(self, workspace_id: str): uri_obj = OssUriObj(oss_storage_uri) return "oss://{}".format(uri_obj.bucket_name) - def set_default_oss_storage(self, workspace_id, bucket_info: SimplifiedBucketInfo): + def set_default_oss_storage( + self, workspace_id, bucket_name: str, intranet_endpoint: str + ): workspace_api = self.get_workspace_api() - oss_uri = "oss://{}.{}/".format(bucket_info.name, bucket_info.intranet_endpoint) + oss_uri = "oss://{}.{}/".format(bucket_name, intranet_endpoint) configs = {WorkspaceConfigKeys.DEFAULT_OSS_STORAGE_URI: oss_uri} workspace_api.update_configs(workspace_id, configs=configs) From 5705a755edd866459932b8381def26af051b61aa Mon Sep 17 00:00:00 2001 From: Yu Zhao Date: Mon, 20 May 2024 16:11:58 +0800 Subject: [PATCH 27/59] feat: RegisteredModel.get_estimator supports selecting training method (#19) * RegisteredModel.get_estimator support training methods --- pai/model.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/pai/model.py b/pai/model.py index 4e5ebb9..c5065b9 100644 --- a/pai/model.py +++ b/pai/model.py @@ -1834,6 +1834,7 @@ def _build_service_config( def get_estimator( self, + training_method: Optional[str] = None, instance_type: Optional[str] = None, instance_count: Optional[int] = None, hyperparameters: Optional[Dict[str, Any]] = None, @@ -1847,6 +1848,9 @@ def get_estimator( Generate an AlgorithmEstimator object from RegisteredModel's training_spec. Args: + training_method (str, optional): Used to selected the training algorithm + that supported by the model. If not specified, the default training + algorithm will be retrieved from the model version. instance_type (str, optional): The machine instance type used to run the training job. If not provider, the default instance type will be retrieved from the algorithm definition. To view the supported machine @@ -1878,6 +1882,35 @@ def get_estimator( "The provided registered model does not contain training spec." ) ts = self.training_spec + if "AlgorithmSpec" not in ts and "AlgorithmName" not in ts: + # Support choosing training methods. + supported_training_methods = list(ts.keys()) + if training_method and training_method not in supported_training_methods: + raise ValueError( + "The model does not support the given training method:" + f" {training_method}. Supported training methods are:" + f" {supported_training_methods}." + ) + elif training_method: + ts = ts.get(training_method) + else: + training_method = supported_training_methods[0] + logger.warning( + "The training method is not specified, using the default training" + " method: %s. Supported training methods are: %s.", + training_method, + supported_training_methods, + ) + ts = ts.get(training_method) + else: + # Does not support training methods. + # Use default training spec. + if training_method: + raise ValueError( + "The model does not support choosing training method. Do not" + " specify the training method." + ) + if "AlgorithmSpec" not in ts and "AlgorithmName" not in ts: raise ValueError( "The provided registered model's training spec does not contain any" From cd5dcbc5737b99d55b3e5cde9576942b69686ad6 Mon Sep 17 00:00:00 2001 From: LiangQuan Date: Mon, 3 Jun 2024 17:50:15 +0800 Subject: [PATCH 28/59] fix pai.toolkit.config in dsw notebook binding with DefaultRole (#22) * vendor pai-dsw sdk * fix pai.toolkit.config in dsw notebook * fix ClientFactory create dsw_client * fix prompt_with_dsw_default_role return * fix get roles in workspace and oss endpoint config --- pai/api/base.py | 1 + pai/api/client_factory.py | 2 + .../alibabacloud_pai_dsw20220101/__init__.py | 1 + .../alibabacloud_pai_dsw20220101/client.py | 2162 ++++++ .../alibabacloud_pai_dsw20220101/models.py | 6444 +++++++++++++++++ pai/toolkit/config.py | 76 +- pai/toolkit/helper/utils.py | 34 +- 7 files changed, 8711 insertions(+), 9 deletions(-) create mode 100644 pai/libs/alibabacloud_pai_dsw20220101/__init__.py create mode 100644 pai/libs/alibabacloud_pai_dsw20220101/client.py create mode 100644 pai/libs/alibabacloud_pai_dsw20220101/models.py diff --git a/pai/api/base.py b/pai/api/base.py index 05bccf2..6b0185a 100644 --- a/pai/api/base.py +++ b/pai/api/base.py @@ -35,6 +35,7 @@ class ServiceName(object): PAIFLOW = "paiflow" # Other services provided by Alibaba Cloud. STS = "sts" + PAI_DSW = "pai-dsw" class PAIRestResourceTypes(object): diff --git a/pai/api/client_factory.py b/pai/api/client_factory.py index 35459d9..73b5483 100644 --- a/pai/api/client_factory.py +++ b/pai/api/client_factory.py @@ -23,6 +23,7 @@ from ..libs.alibabacloud_aiworkspace20210204.client import Client as WorkspaceClient from ..libs.alibabacloud_eas20210701.client import Client as EasClient from ..libs.alibabacloud_pai_dlc20201203.client import Client as DlcClient +from ..libs.alibabacloud_pai_dsw20220101.client import Client as DswClient from ..libs.alibabacloud_paiflow20210202.client import Client as FlowClient from ..libs.alibabacloud_paistudio20220112.client import Client as PaiClient from .base import ServiceName @@ -40,6 +41,7 @@ class ClientFactory(object): ServiceName.PAIFLOW: FlowClient, ServiceName.PAI_STUDIO: PaiClient, ServiceName.STS: StsClient, + ServiceName.PAI_DSW: DswClient, } @staticmethod diff --git a/pai/libs/alibabacloud_pai_dsw20220101/__init__.py b/pai/libs/alibabacloud_pai_dsw20220101/__init__.py new file mode 100644 index 0000000..f94ba41 --- /dev/null +++ b/pai/libs/alibabacloud_pai_dsw20220101/__init__.py @@ -0,0 +1 @@ +__version__ = '1.3.0' \ No newline at end of file diff --git a/pai/libs/alibabacloud_pai_dsw20220101/client.py b/pai/libs/alibabacloud_pai_dsw20220101/client.py new file mode 100644 index 0000000..9ddc7c3 --- /dev/null +++ b/pai/libs/alibabacloud_pai_dsw20220101/client.py @@ -0,0 +1,2162 @@ +# -*- coding: utf-8 -*- +# This file is auto-generated, don't edit it. Thanks. +from typing import Dict +from Tea.core import TeaCore + +from alibabacloud_tea_openapi.client import Client as OpenApiClient +from alibabacloud_tea_openapi import models as open_api_models +from alibabacloud_tea_util.client import Client as UtilClient +from alibabacloud_endpoint_util.client import Client as EndpointUtilClient +from alibabacloud_tea_util import models as util_models +from alibabacloud_openapi_util.client import Client as OpenApiUtilClient + +from pai.libs.alibabacloud_pai_dsw20220101 import models as pai_dsw_20220101_models + +class Client(OpenApiClient): + """ + *\ + """ + def __init__( + self, + config: open_api_models.Config, + ): + super().__init__(config) + self._endpoint_rule = '' + self.check_config(config) + self._endpoint = self.get_endpoint('pai-dsw', self._region_id, self._endpoint_rule, self._network, self._suffix, self._endpoint_map, self._endpoint) + + def get_endpoint( + self, + product_id: str, + region_id: str, + endpoint_rule: str, + network: str, + suffix: str, + endpoint_map: Dict[str, str], + endpoint: str, + ) -> str: + if not UtilClient.empty(endpoint): + return endpoint + if not UtilClient.is_unset(endpoint_map) and not UtilClient.empty(endpoint_map.get(region_id)): + return endpoint_map.get(region_id) + return EndpointUtilClient.get_endpoint_rules(product_id, region_id, endpoint_rule, network, suffix) + + def create_idle_instance_culler_with_options( + self, + instance_id: str, + request: pai_dsw_20220101_models.CreateIdleInstanceCullerRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.CreateIdleInstanceCullerResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.cpu_percent_threshold): + body['CpuPercentThreshold'] = request.cpu_percent_threshold + if not UtilClient.is_unset(request.gpu_percent_threshold): + body['GpuPercentThreshold'] = request.gpu_percent_threshold + if not UtilClient.is_unset(request.max_idle_time_in_minutes): + body['MaxIdleTimeInMinutes'] = request.max_idle_time_in_minutes + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='CreateIdleInstanceCuller', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instances/{OpenApiUtilClient.get_encode_param(instance_id)}/idleinstanceculler', + method='POST', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.CreateIdleInstanceCullerResponse(), + self.call_api(params, req, runtime) + ) + + async def create_idle_instance_culler_with_options_async( + self, + instance_id: str, + request: pai_dsw_20220101_models.CreateIdleInstanceCullerRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.CreateIdleInstanceCullerResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.cpu_percent_threshold): + body['CpuPercentThreshold'] = request.cpu_percent_threshold + if not UtilClient.is_unset(request.gpu_percent_threshold): + body['GpuPercentThreshold'] = request.gpu_percent_threshold + if not UtilClient.is_unset(request.max_idle_time_in_minutes): + body['MaxIdleTimeInMinutes'] = request.max_idle_time_in_minutes + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='CreateIdleInstanceCuller', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instances/{OpenApiUtilClient.get_encode_param(instance_id)}/idleinstanceculler', + method='POST', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.CreateIdleInstanceCullerResponse(), + await self.call_api_async(params, req, runtime) + ) + + def create_idle_instance_culler( + self, + instance_id: str, + request: pai_dsw_20220101_models.CreateIdleInstanceCullerRequest, + ) -> pai_dsw_20220101_models.CreateIdleInstanceCullerResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.create_idle_instance_culler_with_options(instance_id, request, headers, runtime) + + async def create_idle_instance_culler_async( + self, + instance_id: str, + request: pai_dsw_20220101_models.CreateIdleInstanceCullerRequest, + ) -> pai_dsw_20220101_models.CreateIdleInstanceCullerResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.create_idle_instance_culler_with_options_async(instance_id, request, headers, runtime) + + def create_instance_with_options( + self, + request: pai_dsw_20220101_models.CreateInstanceRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.CreateInstanceResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.accessibility): + body['Accessibility'] = request.accessibility + if not UtilClient.is_unset(request.cloud_disks): + body['CloudDisks'] = request.cloud_disks + if not UtilClient.is_unset(request.datasets): + body['Datasets'] = request.datasets + if not UtilClient.is_unset(request.driver): + body['Driver'] = request.driver + if not UtilClient.is_unset(request.ecs_spec): + body['EcsSpec'] = request.ecs_spec + if not UtilClient.is_unset(request.environment_variables): + body['EnvironmentVariables'] = request.environment_variables + if not UtilClient.is_unset(request.image_id): + body['ImageId'] = request.image_id + if not UtilClient.is_unset(request.image_url): + body['ImageUrl'] = request.image_url + if not UtilClient.is_unset(request.instance_name): + body['InstanceName'] = request.instance_name + if not UtilClient.is_unset(request.labels): + body['Labels'] = request.labels + if not UtilClient.is_unset(request.priority): + body['Priority'] = request.priority + if not UtilClient.is_unset(request.requested_resource): + body['RequestedResource'] = request.requested_resource + if not UtilClient.is_unset(request.resource_id): + body['ResourceId'] = request.resource_id + if not UtilClient.is_unset(request.user_id): + body['UserId'] = request.user_id + if not UtilClient.is_unset(request.user_vpc): + body['UserVpc'] = request.user_vpc + if not UtilClient.is_unset(request.workspace_id): + body['WorkspaceId'] = request.workspace_id + if not UtilClient.is_unset(request.workspace_source): + body['WorkspaceSource'] = request.workspace_source + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='CreateInstance', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instances', + method='POST', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.CreateInstanceResponse(), + self.call_api(params, req, runtime) + ) + + async def create_instance_with_options_async( + self, + request: pai_dsw_20220101_models.CreateInstanceRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.CreateInstanceResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.accessibility): + body['Accessibility'] = request.accessibility + if not UtilClient.is_unset(request.cloud_disks): + body['CloudDisks'] = request.cloud_disks + if not UtilClient.is_unset(request.datasets): + body['Datasets'] = request.datasets + if not UtilClient.is_unset(request.driver): + body['Driver'] = request.driver + if not UtilClient.is_unset(request.ecs_spec): + body['EcsSpec'] = request.ecs_spec + if not UtilClient.is_unset(request.environment_variables): + body['EnvironmentVariables'] = request.environment_variables + if not UtilClient.is_unset(request.image_id): + body['ImageId'] = request.image_id + if not UtilClient.is_unset(request.image_url): + body['ImageUrl'] = request.image_url + if not UtilClient.is_unset(request.instance_name): + body['InstanceName'] = request.instance_name + if not UtilClient.is_unset(request.labels): + body['Labels'] = request.labels + if not UtilClient.is_unset(request.priority): + body['Priority'] = request.priority + if not UtilClient.is_unset(request.requested_resource): + body['RequestedResource'] = request.requested_resource + if not UtilClient.is_unset(request.resource_id): + body['ResourceId'] = request.resource_id + if not UtilClient.is_unset(request.user_id): + body['UserId'] = request.user_id + if not UtilClient.is_unset(request.user_vpc): + body['UserVpc'] = request.user_vpc + if not UtilClient.is_unset(request.workspace_id): + body['WorkspaceId'] = request.workspace_id + if not UtilClient.is_unset(request.workspace_source): + body['WorkspaceSource'] = request.workspace_source + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='CreateInstance', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instances', + method='POST', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.CreateInstanceResponse(), + await self.call_api_async(params, req, runtime) + ) + + def create_instance( + self, + request: pai_dsw_20220101_models.CreateInstanceRequest, + ) -> pai_dsw_20220101_models.CreateInstanceResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.create_instance_with_options(request, headers, runtime) + + async def create_instance_async( + self, + request: pai_dsw_20220101_models.CreateInstanceRequest, + ) -> pai_dsw_20220101_models.CreateInstanceResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.create_instance_with_options_async(request, headers, runtime) + + def create_instance_shutdown_timer_with_options( + self, + instance_id: str, + request: pai_dsw_20220101_models.CreateInstanceShutdownTimerRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.CreateInstanceShutdownTimerResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.due_time): + body['DueTime'] = request.due_time + if not UtilClient.is_unset(request.remaining_time_in_ms): + body['RemainingTimeInMs'] = request.remaining_time_in_ms + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='CreateInstanceShutdownTimer', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instances/{OpenApiUtilClient.get_encode_param(instance_id)}/shutdowntimer', + method='POST', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.CreateInstanceShutdownTimerResponse(), + self.call_api(params, req, runtime) + ) + + async def create_instance_shutdown_timer_with_options_async( + self, + instance_id: str, + request: pai_dsw_20220101_models.CreateInstanceShutdownTimerRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.CreateInstanceShutdownTimerResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.due_time): + body['DueTime'] = request.due_time + if not UtilClient.is_unset(request.remaining_time_in_ms): + body['RemainingTimeInMs'] = request.remaining_time_in_ms + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='CreateInstanceShutdownTimer', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instances/{OpenApiUtilClient.get_encode_param(instance_id)}/shutdowntimer', + method='POST', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.CreateInstanceShutdownTimerResponse(), + await self.call_api_async(params, req, runtime) + ) + + def create_instance_shutdown_timer( + self, + instance_id: str, + request: pai_dsw_20220101_models.CreateInstanceShutdownTimerRequest, + ) -> pai_dsw_20220101_models.CreateInstanceShutdownTimerResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.create_instance_shutdown_timer_with_options(instance_id, request, headers, runtime) + + async def create_instance_shutdown_timer_async( + self, + instance_id: str, + request: pai_dsw_20220101_models.CreateInstanceShutdownTimerRequest, + ) -> pai_dsw_20220101_models.CreateInstanceShutdownTimerResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.create_instance_shutdown_timer_with_options_async(instance_id, request, headers, runtime) + + def create_instance_snapshot_with_options( + self, + instance_id: str, + request: pai_dsw_20220101_models.CreateInstanceSnapshotRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.CreateInstanceSnapshotResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.exclude_paths): + body['ExcludePaths'] = request.exclude_paths + if not UtilClient.is_unset(request.image_url): + body['ImageUrl'] = request.image_url + if not UtilClient.is_unset(request.labels): + body['Labels'] = request.labels + if not UtilClient.is_unset(request.overwrite): + body['Overwrite'] = request.overwrite + if not UtilClient.is_unset(request.snapshot_description): + body['SnapshotDescription'] = request.snapshot_description + if not UtilClient.is_unset(request.snapshot_name): + body['SnapshotName'] = request.snapshot_name + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='CreateInstanceSnapshot', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instances/{OpenApiUtilClient.get_encode_param(instance_id)}/snapshots', + method='POST', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.CreateInstanceSnapshotResponse(), + self.call_api(params, req, runtime) + ) + + async def create_instance_snapshot_with_options_async( + self, + instance_id: str, + request: pai_dsw_20220101_models.CreateInstanceSnapshotRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.CreateInstanceSnapshotResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.exclude_paths): + body['ExcludePaths'] = request.exclude_paths + if not UtilClient.is_unset(request.image_url): + body['ImageUrl'] = request.image_url + if not UtilClient.is_unset(request.labels): + body['Labels'] = request.labels + if not UtilClient.is_unset(request.overwrite): + body['Overwrite'] = request.overwrite + if not UtilClient.is_unset(request.snapshot_description): + body['SnapshotDescription'] = request.snapshot_description + if not UtilClient.is_unset(request.snapshot_name): + body['SnapshotName'] = request.snapshot_name + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='CreateInstanceSnapshot', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instances/{OpenApiUtilClient.get_encode_param(instance_id)}/snapshots', + method='POST', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.CreateInstanceSnapshotResponse(), + await self.call_api_async(params, req, runtime) + ) + + def create_instance_snapshot( + self, + instance_id: str, + request: pai_dsw_20220101_models.CreateInstanceSnapshotRequest, + ) -> pai_dsw_20220101_models.CreateInstanceSnapshotResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.create_instance_snapshot_with_options(instance_id, request, headers, runtime) + + async def create_instance_snapshot_async( + self, + instance_id: str, + request: pai_dsw_20220101_models.CreateInstanceSnapshotRequest, + ) -> pai_dsw_20220101_models.CreateInstanceSnapshotResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.create_instance_snapshot_with_options_async(instance_id, request, headers, runtime) + + def delete_idle_instance_culler_with_options( + self, + instance_id: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.DeleteIdleInstanceCullerResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='DeleteIdleInstanceCuller', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instances/{OpenApiUtilClient.get_encode_param(instance_id)}/idleinstanceculler', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.DeleteIdleInstanceCullerResponse(), + self.call_api(params, req, runtime) + ) + + async def delete_idle_instance_culler_with_options_async( + self, + instance_id: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.DeleteIdleInstanceCullerResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='DeleteIdleInstanceCuller', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instances/{OpenApiUtilClient.get_encode_param(instance_id)}/idleinstanceculler', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.DeleteIdleInstanceCullerResponse(), + await self.call_api_async(params, req, runtime) + ) + + def delete_idle_instance_culler( + self, + instance_id: str, + ) -> pai_dsw_20220101_models.DeleteIdleInstanceCullerResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.delete_idle_instance_culler_with_options(instance_id, headers, runtime) + + async def delete_idle_instance_culler_async( + self, + instance_id: str, + ) -> pai_dsw_20220101_models.DeleteIdleInstanceCullerResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.delete_idle_instance_culler_with_options_async(instance_id, headers, runtime) + + def delete_instance_with_options( + self, + instance_id: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.DeleteInstanceResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='DeleteInstance', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instances/{OpenApiUtilClient.get_encode_param(instance_id)}', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.DeleteInstanceResponse(), + self.call_api(params, req, runtime) + ) + + async def delete_instance_with_options_async( + self, + instance_id: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.DeleteInstanceResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='DeleteInstance', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instances/{OpenApiUtilClient.get_encode_param(instance_id)}', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.DeleteInstanceResponse(), + await self.call_api_async(params, req, runtime) + ) + + def delete_instance( + self, + instance_id: str, + ) -> pai_dsw_20220101_models.DeleteInstanceResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.delete_instance_with_options(instance_id, headers, runtime) + + async def delete_instance_async( + self, + instance_id: str, + ) -> pai_dsw_20220101_models.DeleteInstanceResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.delete_instance_with_options_async(instance_id, headers, runtime) + + def delete_instance_shutdown_timer_with_options( + self, + instance_id: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.DeleteInstanceShutdownTimerResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='DeleteInstanceShutdownTimer', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instances/{OpenApiUtilClient.get_encode_param(instance_id)}/shutdowntimer', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.DeleteInstanceShutdownTimerResponse(), + self.call_api(params, req, runtime) + ) + + async def delete_instance_shutdown_timer_with_options_async( + self, + instance_id: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.DeleteInstanceShutdownTimerResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='DeleteInstanceShutdownTimer', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instances/{OpenApiUtilClient.get_encode_param(instance_id)}/shutdowntimer', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.DeleteInstanceShutdownTimerResponse(), + await self.call_api_async(params, req, runtime) + ) + + def delete_instance_shutdown_timer( + self, + instance_id: str, + ) -> pai_dsw_20220101_models.DeleteInstanceShutdownTimerResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.delete_instance_shutdown_timer_with_options(instance_id, headers, runtime) + + async def delete_instance_shutdown_timer_async( + self, + instance_id: str, + ) -> pai_dsw_20220101_models.DeleteInstanceShutdownTimerResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.delete_instance_shutdown_timer_with_options_async(instance_id, headers, runtime) + + def delete_instance_snapshot_with_options( + self, + instance_id: str, + snapshot_id: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.DeleteInstanceSnapshotResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='DeleteInstanceSnapshot', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instances/{OpenApiUtilClient.get_encode_param(instance_id)}/snapshots/{OpenApiUtilClient.get_encode_param(snapshot_id)}', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.DeleteInstanceSnapshotResponse(), + self.call_api(params, req, runtime) + ) + + async def delete_instance_snapshot_with_options_async( + self, + instance_id: str, + snapshot_id: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.DeleteInstanceSnapshotResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='DeleteInstanceSnapshot', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instances/{OpenApiUtilClient.get_encode_param(instance_id)}/snapshots/{OpenApiUtilClient.get_encode_param(snapshot_id)}', + method='DELETE', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.DeleteInstanceSnapshotResponse(), + await self.call_api_async(params, req, runtime) + ) + + def delete_instance_snapshot( + self, + instance_id: str, + snapshot_id: str, + ) -> pai_dsw_20220101_models.DeleteInstanceSnapshotResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.delete_instance_snapshot_with_options(instance_id, snapshot_id, headers, runtime) + + async def delete_instance_snapshot_async( + self, + instance_id: str, + snapshot_id: str, + ) -> pai_dsw_20220101_models.DeleteInstanceSnapshotResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.delete_instance_snapshot_with_options_async(instance_id, snapshot_id, headers, runtime) + + def get_idle_instance_culler_with_options( + self, + instance_id: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.GetIdleInstanceCullerResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='GetIdleInstanceCuller', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instances/{OpenApiUtilClient.get_encode_param(instance_id)}/idleinstanceculler', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.GetIdleInstanceCullerResponse(), + self.call_api(params, req, runtime) + ) + + async def get_idle_instance_culler_with_options_async( + self, + instance_id: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.GetIdleInstanceCullerResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='GetIdleInstanceCuller', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instances/{OpenApiUtilClient.get_encode_param(instance_id)}/idleinstanceculler', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.GetIdleInstanceCullerResponse(), + await self.call_api_async(params, req, runtime) + ) + + def get_idle_instance_culler( + self, + instance_id: str, + ) -> pai_dsw_20220101_models.GetIdleInstanceCullerResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.get_idle_instance_culler_with_options(instance_id, headers, runtime) + + async def get_idle_instance_culler_async( + self, + instance_id: str, + ) -> pai_dsw_20220101_models.GetIdleInstanceCullerResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.get_idle_instance_culler_with_options_async(instance_id, headers, runtime) + + def get_instance_with_options( + self, + instance_id: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.GetInstanceResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='GetInstance', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instances/{OpenApiUtilClient.get_encode_param(instance_id)}', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.GetInstanceResponse(), + self.call_api(params, req, runtime) + ) + + async def get_instance_with_options_async( + self, + instance_id: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.GetInstanceResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='GetInstance', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instances/{OpenApiUtilClient.get_encode_param(instance_id)}', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.GetInstanceResponse(), + await self.call_api_async(params, req, runtime) + ) + + def get_instance( + self, + instance_id: str, + ) -> pai_dsw_20220101_models.GetInstanceResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.get_instance_with_options(instance_id, headers, runtime) + + async def get_instance_async( + self, + instance_id: str, + ) -> pai_dsw_20220101_models.GetInstanceResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.get_instance_with_options_async(instance_id, headers, runtime) + + def get_instance_events_with_options( + self, + instance_id: str, + request: pai_dsw_20220101_models.GetInstanceEventsRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.GetInstanceEventsResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.end_time): + query['EndTime'] = request.end_time + if not UtilClient.is_unset(request.max_events_num): + query['MaxEventsNum'] = request.max_events_num + if not UtilClient.is_unset(request.start_time): + query['StartTime'] = request.start_time + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='GetInstanceEvents', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instances/{OpenApiUtilClient.get_encode_param(instance_id)}/events', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.GetInstanceEventsResponse(), + self.call_api(params, req, runtime) + ) + + async def get_instance_events_with_options_async( + self, + instance_id: str, + request: pai_dsw_20220101_models.GetInstanceEventsRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.GetInstanceEventsResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.end_time): + query['EndTime'] = request.end_time + if not UtilClient.is_unset(request.max_events_num): + query['MaxEventsNum'] = request.max_events_num + if not UtilClient.is_unset(request.start_time): + query['StartTime'] = request.start_time + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='GetInstanceEvents', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instances/{OpenApiUtilClient.get_encode_param(instance_id)}/events', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.GetInstanceEventsResponse(), + await self.call_api_async(params, req, runtime) + ) + + def get_instance_events( + self, + instance_id: str, + request: pai_dsw_20220101_models.GetInstanceEventsRequest, + ) -> pai_dsw_20220101_models.GetInstanceEventsResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.get_instance_events_with_options(instance_id, request, headers, runtime) + + async def get_instance_events_async( + self, + instance_id: str, + request: pai_dsw_20220101_models.GetInstanceEventsRequest, + ) -> pai_dsw_20220101_models.GetInstanceEventsResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.get_instance_events_with_options_async(instance_id, request, headers, runtime) + + def get_instance_metrics_with_options( + self, + instance_id: str, + request: pai_dsw_20220101_models.GetInstanceMetricsRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.GetInstanceMetricsResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.end_time): + query['EndTime'] = request.end_time + if not UtilClient.is_unset(request.metric_type): + query['MetricType'] = request.metric_type + if not UtilClient.is_unset(request.start_time): + query['StartTime'] = request.start_time + if not UtilClient.is_unset(request.time_step): + query['TimeStep'] = request.time_step + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='GetInstanceMetrics', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instance/{OpenApiUtilClient.get_encode_param(instance_id)}/metrics', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.GetInstanceMetricsResponse(), + self.call_api(params, req, runtime) + ) + + async def get_instance_metrics_with_options_async( + self, + instance_id: str, + request: pai_dsw_20220101_models.GetInstanceMetricsRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.GetInstanceMetricsResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.end_time): + query['EndTime'] = request.end_time + if not UtilClient.is_unset(request.metric_type): + query['MetricType'] = request.metric_type + if not UtilClient.is_unset(request.start_time): + query['StartTime'] = request.start_time + if not UtilClient.is_unset(request.time_step): + query['TimeStep'] = request.time_step + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='GetInstanceMetrics', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instance/{OpenApiUtilClient.get_encode_param(instance_id)}/metrics', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.GetInstanceMetricsResponse(), + await self.call_api_async(params, req, runtime) + ) + + def get_instance_metrics( + self, + instance_id: str, + request: pai_dsw_20220101_models.GetInstanceMetricsRequest, + ) -> pai_dsw_20220101_models.GetInstanceMetricsResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.get_instance_metrics_with_options(instance_id, request, headers, runtime) + + async def get_instance_metrics_async( + self, + instance_id: str, + request: pai_dsw_20220101_models.GetInstanceMetricsRequest, + ) -> pai_dsw_20220101_models.GetInstanceMetricsResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.get_instance_metrics_with_options_async(instance_id, request, headers, runtime) + + def get_instance_shutdown_timer_with_options( + self, + instance_id: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.GetInstanceShutdownTimerResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='GetInstanceShutdownTimer', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instances/{OpenApiUtilClient.get_encode_param(instance_id)}/shutdowntimer', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.GetInstanceShutdownTimerResponse(), + self.call_api(params, req, runtime) + ) + + async def get_instance_shutdown_timer_with_options_async( + self, + instance_id: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.GetInstanceShutdownTimerResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='GetInstanceShutdownTimer', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instances/{OpenApiUtilClient.get_encode_param(instance_id)}/shutdowntimer', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.GetInstanceShutdownTimerResponse(), + await self.call_api_async(params, req, runtime) + ) + + def get_instance_shutdown_timer( + self, + instance_id: str, + ) -> pai_dsw_20220101_models.GetInstanceShutdownTimerResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.get_instance_shutdown_timer_with_options(instance_id, headers, runtime) + + async def get_instance_shutdown_timer_async( + self, + instance_id: str, + ) -> pai_dsw_20220101_models.GetInstanceShutdownTimerResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.get_instance_shutdown_timer_with_options_async(instance_id, headers, runtime) + + def get_instance_snapshot_with_options( + self, + instance_id: str, + snapshot_id: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.GetInstanceSnapshotResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='GetInstanceSnapshot', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instances/{OpenApiUtilClient.get_encode_param(instance_id)}/snapshots/{OpenApiUtilClient.get_encode_param(snapshot_id)}', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.GetInstanceSnapshotResponse(), + self.call_api(params, req, runtime) + ) + + async def get_instance_snapshot_with_options_async( + self, + instance_id: str, + snapshot_id: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.GetInstanceSnapshotResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='GetInstanceSnapshot', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instances/{OpenApiUtilClient.get_encode_param(instance_id)}/snapshots/{OpenApiUtilClient.get_encode_param(snapshot_id)}', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.GetInstanceSnapshotResponse(), + await self.call_api_async(params, req, runtime) + ) + + def get_instance_snapshot( + self, + instance_id: str, + snapshot_id: str, + ) -> pai_dsw_20220101_models.GetInstanceSnapshotResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.get_instance_snapshot_with_options(instance_id, snapshot_id, headers, runtime) + + async def get_instance_snapshot_async( + self, + instance_id: str, + snapshot_id: str, + ) -> pai_dsw_20220101_models.GetInstanceSnapshotResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.get_instance_snapshot_with_options_async(instance_id, snapshot_id, headers, runtime) + + def get_lifecycle_with_options( + self, + instance_id: str, + request: pai_dsw_20220101_models.GetLifecycleRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.GetLifecycleResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.end_time): + query['EndTime'] = request.end_time + if not UtilClient.is_unset(request.limit): + query['Limit'] = request.limit + if not UtilClient.is_unset(request.order): + query['Order'] = request.order + if not UtilClient.is_unset(request.session_number): + query['SessionNumber'] = request.session_number + if not UtilClient.is_unset(request.start_time): + query['StartTime'] = request.start_time + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='GetLifecycle', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instances/{OpenApiUtilClient.get_encode_param(instance_id)}/lifecycle', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.GetLifecycleResponse(), + self.call_api(params, req, runtime) + ) + + async def get_lifecycle_with_options_async( + self, + instance_id: str, + request: pai_dsw_20220101_models.GetLifecycleRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.GetLifecycleResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.end_time): + query['EndTime'] = request.end_time + if not UtilClient.is_unset(request.limit): + query['Limit'] = request.limit + if not UtilClient.is_unset(request.order): + query['Order'] = request.order + if not UtilClient.is_unset(request.session_number): + query['SessionNumber'] = request.session_number + if not UtilClient.is_unset(request.start_time): + query['StartTime'] = request.start_time + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='GetLifecycle', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instances/{OpenApiUtilClient.get_encode_param(instance_id)}/lifecycle', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.GetLifecycleResponse(), + await self.call_api_async(params, req, runtime) + ) + + def get_lifecycle( + self, + instance_id: str, + request: pai_dsw_20220101_models.GetLifecycleRequest, + ) -> pai_dsw_20220101_models.GetLifecycleResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.get_lifecycle_with_options(instance_id, request, headers, runtime) + + async def get_lifecycle_async( + self, + instance_id: str, + request: pai_dsw_20220101_models.GetLifecycleRequest, + ) -> pai_dsw_20220101_models.GetLifecycleResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.get_lifecycle_with_options_async(instance_id, request, headers, runtime) + + def get_resource_group_statistics_with_options( + self, + request: pai_dsw_20220101_models.GetResourceGroupStatisticsRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.GetResourceGroupStatisticsResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.end_time): + query['EndTime'] = request.end_time + if not UtilClient.is_unset(request.resource_id): + query['ResourceId'] = request.resource_id + if not UtilClient.is_unset(request.start_time): + query['StartTime'] = request.start_time + if not UtilClient.is_unset(request.workspace_ids): + query['WorkspaceIds'] = request.workspace_ids + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='GetResourceGroupStatistics', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/resourcegroupstatistics', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.GetResourceGroupStatisticsResponse(), + self.call_api(params, req, runtime) + ) + + async def get_resource_group_statistics_with_options_async( + self, + request: pai_dsw_20220101_models.GetResourceGroupStatisticsRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.GetResourceGroupStatisticsResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.end_time): + query['EndTime'] = request.end_time + if not UtilClient.is_unset(request.resource_id): + query['ResourceId'] = request.resource_id + if not UtilClient.is_unset(request.start_time): + query['StartTime'] = request.start_time + if not UtilClient.is_unset(request.workspace_ids): + query['WorkspaceIds'] = request.workspace_ids + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='GetResourceGroupStatistics', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/resourcegroupstatistics', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.GetResourceGroupStatisticsResponse(), + await self.call_api_async(params, req, runtime) + ) + + def get_resource_group_statistics( + self, + request: pai_dsw_20220101_models.GetResourceGroupStatisticsRequest, + ) -> pai_dsw_20220101_models.GetResourceGroupStatisticsResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.get_resource_group_statistics_with_options(request, headers, runtime) + + async def get_resource_group_statistics_async( + self, + request: pai_dsw_20220101_models.GetResourceGroupStatisticsRequest, + ) -> pai_dsw_20220101_models.GetResourceGroupStatisticsResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.get_resource_group_statistics_with_options_async(request, headers, runtime) + + def get_token_with_options( + self, + request: pai_dsw_20220101_models.GetTokenRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.GetTokenResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.expire_time): + query['ExpireTime'] = request.expire_time + if not UtilClient.is_unset(request.instance_id): + query['InstanceId'] = request.instance_id + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='GetToken', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/tokens', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.GetTokenResponse(), + self.call_api(params, req, runtime) + ) + + async def get_token_with_options_async( + self, + request: pai_dsw_20220101_models.GetTokenRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.GetTokenResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.expire_time): + query['ExpireTime'] = request.expire_time + if not UtilClient.is_unset(request.instance_id): + query['InstanceId'] = request.instance_id + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='GetToken', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/tokens', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.GetTokenResponse(), + await self.call_api_async(params, req, runtime) + ) + + def get_token( + self, + request: pai_dsw_20220101_models.GetTokenRequest, + ) -> pai_dsw_20220101_models.GetTokenResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.get_token_with_options(request, headers, runtime) + + async def get_token_async( + self, + request: pai_dsw_20220101_models.GetTokenRequest, + ) -> pai_dsw_20220101_models.GetTokenResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.get_token_with_options_async(request, headers, runtime) + + def get_user_config_with_options( + self, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.GetUserConfigResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='GetUserConfig', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/userconfig', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.GetUserConfigResponse(), + self.call_api(params, req, runtime) + ) + + async def get_user_config_with_options_async( + self, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.GetUserConfigResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='GetUserConfig', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/userconfig', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.GetUserConfigResponse(), + await self.call_api_async(params, req, runtime) + ) + + def get_user_config(self) -> pai_dsw_20220101_models.GetUserConfigResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.get_user_config_with_options(headers, runtime) + + async def get_user_config_async(self) -> pai_dsw_20220101_models.GetUserConfigResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.get_user_config_with_options_async(headers, runtime) + + def list_ecs_specs_with_options( + self, + request: pai_dsw_20220101_models.ListEcsSpecsRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.ListEcsSpecsResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.accelerator_type): + query['AcceleratorType'] = request.accelerator_type + if not UtilClient.is_unset(request.order): + query['Order'] = request.order + if not UtilClient.is_unset(request.page_number): + query['PageNumber'] = request.page_number + if not UtilClient.is_unset(request.page_size): + query['PageSize'] = request.page_size + if not UtilClient.is_unset(request.sort_by): + query['SortBy'] = request.sort_by + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='ListEcsSpecs', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/ecsspecs', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.ListEcsSpecsResponse(), + self.call_api(params, req, runtime) + ) + + async def list_ecs_specs_with_options_async( + self, + request: pai_dsw_20220101_models.ListEcsSpecsRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.ListEcsSpecsResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.accelerator_type): + query['AcceleratorType'] = request.accelerator_type + if not UtilClient.is_unset(request.order): + query['Order'] = request.order + if not UtilClient.is_unset(request.page_number): + query['PageNumber'] = request.page_number + if not UtilClient.is_unset(request.page_size): + query['PageSize'] = request.page_size + if not UtilClient.is_unset(request.sort_by): + query['SortBy'] = request.sort_by + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='ListEcsSpecs', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/ecsspecs', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.ListEcsSpecsResponse(), + await self.call_api_async(params, req, runtime) + ) + + def list_ecs_specs( + self, + request: pai_dsw_20220101_models.ListEcsSpecsRequest, + ) -> pai_dsw_20220101_models.ListEcsSpecsResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.list_ecs_specs_with_options(request, headers, runtime) + + async def list_ecs_specs_async( + self, + request: pai_dsw_20220101_models.ListEcsSpecsRequest, + ) -> pai_dsw_20220101_models.ListEcsSpecsResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.list_ecs_specs_with_options_async(request, headers, runtime) + + def list_instance_snapshot_with_options( + self, + instance_id: str, + request: pai_dsw_20220101_models.ListInstanceSnapshotRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.ListInstanceSnapshotResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.order): + query['Order'] = request.order + if not UtilClient.is_unset(request.page_number): + query['PageNumber'] = request.page_number + if not UtilClient.is_unset(request.page_size): + query['PageSize'] = request.page_size + if not UtilClient.is_unset(request.sort_by): + query['SortBy'] = request.sort_by + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='ListInstanceSnapshot', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instances/{OpenApiUtilClient.get_encode_param(instance_id)}/snapshots', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.ListInstanceSnapshotResponse(), + self.call_api(params, req, runtime) + ) + + async def list_instance_snapshot_with_options_async( + self, + instance_id: str, + request: pai_dsw_20220101_models.ListInstanceSnapshotRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.ListInstanceSnapshotResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.order): + query['Order'] = request.order + if not UtilClient.is_unset(request.page_number): + query['PageNumber'] = request.page_number + if not UtilClient.is_unset(request.page_size): + query['PageSize'] = request.page_size + if not UtilClient.is_unset(request.sort_by): + query['SortBy'] = request.sort_by + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='ListInstanceSnapshot', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instances/{OpenApiUtilClient.get_encode_param(instance_id)}/snapshots', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.ListInstanceSnapshotResponse(), + await self.call_api_async(params, req, runtime) + ) + + def list_instance_snapshot( + self, + instance_id: str, + request: pai_dsw_20220101_models.ListInstanceSnapshotRequest, + ) -> pai_dsw_20220101_models.ListInstanceSnapshotResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.list_instance_snapshot_with_options(instance_id, request, headers, runtime) + + async def list_instance_snapshot_async( + self, + instance_id: str, + request: pai_dsw_20220101_models.ListInstanceSnapshotRequest, + ) -> pai_dsw_20220101_models.ListInstanceSnapshotResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.list_instance_snapshot_with_options_async(instance_id, request, headers, runtime) + + def list_instance_statistics_with_options( + self, + request: pai_dsw_20220101_models.ListInstanceStatisticsRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.ListInstanceStatisticsResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.workspace_ids): + query['WorkspaceIds'] = request.workspace_ids + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='ListInstanceStatistics', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instancestatistics', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.ListInstanceStatisticsResponse(), + self.call_api(params, req, runtime) + ) + + async def list_instance_statistics_with_options_async( + self, + request: pai_dsw_20220101_models.ListInstanceStatisticsRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.ListInstanceStatisticsResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.workspace_ids): + query['WorkspaceIds'] = request.workspace_ids + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='ListInstanceStatistics', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instancestatistics', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.ListInstanceStatisticsResponse(), + await self.call_api_async(params, req, runtime) + ) + + def list_instance_statistics( + self, + request: pai_dsw_20220101_models.ListInstanceStatisticsRequest, + ) -> pai_dsw_20220101_models.ListInstanceStatisticsResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.list_instance_statistics_with_options(request, headers, runtime) + + async def list_instance_statistics_async( + self, + request: pai_dsw_20220101_models.ListInstanceStatisticsRequest, + ) -> pai_dsw_20220101_models.ListInstanceStatisticsResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.list_instance_statistics_with_options_async(request, headers, runtime) + + def list_instances_with_options( + self, + request: pai_dsw_20220101_models.ListInstancesRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.ListInstancesResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.accelerator_type): + query['AcceleratorType'] = request.accelerator_type + if not UtilClient.is_unset(request.accessibility): + query['Accessibility'] = request.accessibility + if not UtilClient.is_unset(request.instance_id): + query['InstanceId'] = request.instance_id + if not UtilClient.is_unset(request.instance_name): + query['InstanceName'] = request.instance_name + if not UtilClient.is_unset(request.order): + query['Order'] = request.order + if not UtilClient.is_unset(request.page_number): + query['PageNumber'] = request.page_number + if not UtilClient.is_unset(request.page_size): + query['PageSize'] = request.page_size + if not UtilClient.is_unset(request.payment_type): + query['PaymentType'] = request.payment_type + if not UtilClient.is_unset(request.resource_id): + query['ResourceId'] = request.resource_id + if not UtilClient.is_unset(request.sort_by): + query['SortBy'] = request.sort_by + if not UtilClient.is_unset(request.status): + query['Status'] = request.status + if not UtilClient.is_unset(request.workspace_id): + query['WorkspaceId'] = request.workspace_id + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='ListInstances', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instances', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.ListInstancesResponse(), + self.call_api(params, req, runtime) + ) + + async def list_instances_with_options_async( + self, + request: pai_dsw_20220101_models.ListInstancesRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.ListInstancesResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.accelerator_type): + query['AcceleratorType'] = request.accelerator_type + if not UtilClient.is_unset(request.accessibility): + query['Accessibility'] = request.accessibility + if not UtilClient.is_unset(request.instance_id): + query['InstanceId'] = request.instance_id + if not UtilClient.is_unset(request.instance_name): + query['InstanceName'] = request.instance_name + if not UtilClient.is_unset(request.order): + query['Order'] = request.order + if not UtilClient.is_unset(request.page_number): + query['PageNumber'] = request.page_number + if not UtilClient.is_unset(request.page_size): + query['PageSize'] = request.page_size + if not UtilClient.is_unset(request.payment_type): + query['PaymentType'] = request.payment_type + if not UtilClient.is_unset(request.resource_id): + query['ResourceId'] = request.resource_id + if not UtilClient.is_unset(request.sort_by): + query['SortBy'] = request.sort_by + if not UtilClient.is_unset(request.status): + query['Status'] = request.status + if not UtilClient.is_unset(request.workspace_id): + query['WorkspaceId'] = request.workspace_id + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='ListInstances', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instances', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.ListInstancesResponse(), + await self.call_api_async(params, req, runtime) + ) + + def list_instances( + self, + request: pai_dsw_20220101_models.ListInstancesRequest, + ) -> pai_dsw_20220101_models.ListInstancesResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.list_instances_with_options(request, headers, runtime) + + async def list_instances_async( + self, + request: pai_dsw_20220101_models.ListInstancesRequest, + ) -> pai_dsw_20220101_models.ListInstancesResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.list_instances_with_options_async(request, headers, runtime) + + def start_instance_with_options( + self, + instance_id: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.StartInstanceResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='StartInstance', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instances/{OpenApiUtilClient.get_encode_param(instance_id)}/start', + method='PUT', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.StartInstanceResponse(), + self.call_api(params, req, runtime) + ) + + async def start_instance_with_options_async( + self, + instance_id: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.StartInstanceResponse: + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='StartInstance', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instances/{OpenApiUtilClient.get_encode_param(instance_id)}/start', + method='PUT', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.StartInstanceResponse(), + await self.call_api_async(params, req, runtime) + ) + + def start_instance( + self, + instance_id: str, + ) -> pai_dsw_20220101_models.StartInstanceResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.start_instance_with_options(instance_id, headers, runtime) + + async def start_instance_async( + self, + instance_id: str, + ) -> pai_dsw_20220101_models.StartInstanceResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.start_instance_with_options_async(instance_id, headers, runtime) + + def stop_instance_with_options( + self, + instance_id: str, + request: pai_dsw_20220101_models.StopInstanceRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.StopInstanceResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.save_image): + query['SaveImage'] = request.save_image + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='StopInstance', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instances/{OpenApiUtilClient.get_encode_param(instance_id)}/stop', + method='PUT', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.StopInstanceResponse(), + self.call_api(params, req, runtime) + ) + + async def stop_instance_with_options_async( + self, + instance_id: str, + request: pai_dsw_20220101_models.StopInstanceRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.StopInstanceResponse: + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.save_image): + query['SaveImage'] = request.save_image + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='StopInstance', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instances/{OpenApiUtilClient.get_encode_param(instance_id)}/stop', + method='PUT', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.StopInstanceResponse(), + await self.call_api_async(params, req, runtime) + ) + + def stop_instance( + self, + instance_id: str, + request: pai_dsw_20220101_models.StopInstanceRequest, + ) -> pai_dsw_20220101_models.StopInstanceResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.stop_instance_with_options(instance_id, request, headers, runtime) + + async def stop_instance_async( + self, + instance_id: str, + request: pai_dsw_20220101_models.StopInstanceRequest, + ) -> pai_dsw_20220101_models.StopInstanceResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.stop_instance_with_options_async(instance_id, request, headers, runtime) + + def update_instance_with_options( + self, + instance_id: str, + request: pai_dsw_20220101_models.UpdateInstanceRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.UpdateInstanceResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.accessibility): + body['Accessibility'] = request.accessibility + if not UtilClient.is_unset(request.cloud_disks): + body['CloudDisks'] = request.cloud_disks + if not UtilClient.is_unset(request.datasets): + body['Datasets'] = request.datasets + if not UtilClient.is_unset(request.disassociate_datasets): + body['DisassociateDatasets'] = request.disassociate_datasets + if not UtilClient.is_unset(request.disassociate_driver): + body['DisassociateDriver'] = request.disassociate_driver + if not UtilClient.is_unset(request.disassociate_forward_infos): + body['DisassociateForwardInfos'] = request.disassociate_forward_infos + if not UtilClient.is_unset(request.disassociate_vpc): + body['DisassociateVpc'] = request.disassociate_vpc + if not UtilClient.is_unset(request.driver): + body['Driver'] = request.driver + if not UtilClient.is_unset(request.ecs_spec): + body['EcsSpec'] = request.ecs_spec + if not UtilClient.is_unset(request.image_id): + body['ImageId'] = request.image_id + if not UtilClient.is_unset(request.image_url): + body['ImageUrl'] = request.image_url + if not UtilClient.is_unset(request.instance_name): + body['InstanceName'] = request.instance_name + if not UtilClient.is_unset(request.priority): + body['Priority'] = request.priority + if not UtilClient.is_unset(request.requested_resource): + body['RequestedResource'] = request.requested_resource + if not UtilClient.is_unset(request.user_id): + body['UserId'] = request.user_id + if not UtilClient.is_unset(request.user_vpc): + body['UserVpc'] = request.user_vpc + if not UtilClient.is_unset(request.workspace_source): + body['WorkspaceSource'] = request.workspace_source + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='UpdateInstance', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instances/{OpenApiUtilClient.get_encode_param(instance_id)}', + method='PUT', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.UpdateInstanceResponse(), + self.call_api(params, req, runtime) + ) + + async def update_instance_with_options_async( + self, + instance_id: str, + request: pai_dsw_20220101_models.UpdateInstanceRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_dsw_20220101_models.UpdateInstanceResponse: + UtilClient.validate_model(request) + body = {} + if not UtilClient.is_unset(request.accessibility): + body['Accessibility'] = request.accessibility + if not UtilClient.is_unset(request.cloud_disks): + body['CloudDisks'] = request.cloud_disks + if not UtilClient.is_unset(request.datasets): + body['Datasets'] = request.datasets + if not UtilClient.is_unset(request.disassociate_datasets): + body['DisassociateDatasets'] = request.disassociate_datasets + if not UtilClient.is_unset(request.disassociate_driver): + body['DisassociateDriver'] = request.disassociate_driver + if not UtilClient.is_unset(request.disassociate_forward_infos): + body['DisassociateForwardInfos'] = request.disassociate_forward_infos + if not UtilClient.is_unset(request.disassociate_vpc): + body['DisassociateVpc'] = request.disassociate_vpc + if not UtilClient.is_unset(request.driver): + body['Driver'] = request.driver + if not UtilClient.is_unset(request.ecs_spec): + body['EcsSpec'] = request.ecs_spec + if not UtilClient.is_unset(request.image_id): + body['ImageId'] = request.image_id + if not UtilClient.is_unset(request.image_url): + body['ImageUrl'] = request.image_url + if not UtilClient.is_unset(request.instance_name): + body['InstanceName'] = request.instance_name + if not UtilClient.is_unset(request.priority): + body['Priority'] = request.priority + if not UtilClient.is_unset(request.requested_resource): + body['RequestedResource'] = request.requested_resource + if not UtilClient.is_unset(request.user_id): + body['UserId'] = request.user_id + if not UtilClient.is_unset(request.user_vpc): + body['UserVpc'] = request.user_vpc + if not UtilClient.is_unset(request.workspace_source): + body['WorkspaceSource'] = request.workspace_source + req = open_api_models.OpenApiRequest( + headers=headers, + body=OpenApiUtilClient.parse_to_map(body) + ) + params = open_api_models.Params( + action='UpdateInstance', + version='2022-01-01', + protocol='HTTPS', + pathname=f'/api/v2/instances/{OpenApiUtilClient.get_encode_param(instance_id)}', + method='PUT', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_dsw_20220101_models.UpdateInstanceResponse(), + await self.call_api_async(params, req, runtime) + ) + + def update_instance( + self, + instance_id: str, + request: pai_dsw_20220101_models.UpdateInstanceRequest, + ) -> pai_dsw_20220101_models.UpdateInstanceResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return self.update_instance_with_options(instance_id, request, headers, runtime) + + async def update_instance_async( + self, + instance_id: str, + request: pai_dsw_20220101_models.UpdateInstanceRequest, + ) -> pai_dsw_20220101_models.UpdateInstanceResponse: + runtime = util_models.RuntimeOptions() + headers = {} + return await self.update_instance_with_options_async(instance_id, request, headers, runtime) diff --git a/pai/libs/alibabacloud_pai_dsw20220101/models.py b/pai/libs/alibabacloud_pai_dsw20220101/models.py new file mode 100644 index 0000000..53599e2 --- /dev/null +++ b/pai/libs/alibabacloud_pai_dsw20220101/models.py @@ -0,0 +1,6444 @@ +# -*- coding: utf-8 -*- +# This file is auto-generated, don't edit it. Thanks. +from Tea.model import TeaModel +from typing import List, Dict + + +class DemoCategory(TeaModel): + def __init__( + self, + category_code: str = None, + category_name: str = None, + order: int = None, + sub_categories: List['DemoCategory'] = None, + ): + self.category_code = category_code + self.category_name = category_name + self.order = order + self.sub_categories = sub_categories + + def validate(self): + if self.sub_categories: + for k in self.sub_categories: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.category_code is not None: + result['CategoryCode'] = self.category_code + if self.category_name is not None: + result['CategoryName'] = self.category_name + if self.order is not None: + result['Order'] = self.order + result['SubCategories'] = [] + if self.sub_categories is not None: + for k in self.sub_categories: + result['SubCategories'].append(k.to_map() if k else None) + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('CategoryCode') is not None: + self.category_code = m.get('CategoryCode') + if m.get('CategoryName') is not None: + self.category_name = m.get('CategoryName') + if m.get('Order') is not None: + self.order = m.get('Order') + self.sub_categories = [] + if m.get('SubCategories') is not None: + for k in m.get('SubCategories'): + temp_model = DemoCategory() + self.sub_categories.append(temp_model.from_map(k)) + return self + + +class ForwardInfo(TeaModel): + def __init__( + self, + container_name: str = None, + eip_allocation_id: str = None, + enable: bool = None, + nat_gateway_id: str = None, + port: str = None, + sshpublic_key: str = None, + ): + self.container_name = container_name + self.eip_allocation_id = eip_allocation_id + self.enable = enable + self.nat_gateway_id = nat_gateway_id + self.port = port + self.sshpublic_key = sshpublic_key + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.container_name is not None: + result['ContainerName'] = self.container_name + if self.eip_allocation_id is not None: + result['EipAllocationId'] = self.eip_allocation_id + if self.enable is not None: + result['Enable'] = self.enable + if self.nat_gateway_id is not None: + result['NatGatewayId'] = self.nat_gateway_id + if self.port is not None: + result['Port'] = self.port + if self.sshpublic_key is not None: + result['SSHPublicKey'] = self.sshpublic_key + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('ContainerName') is not None: + self.container_name = m.get('ContainerName') + if m.get('EipAllocationId') is not None: + self.eip_allocation_id = m.get('EipAllocationId') + if m.get('Enable') is not None: + self.enable = m.get('Enable') + if m.get('NatGatewayId') is not None: + self.nat_gateway_id = m.get('NatGatewayId') + if m.get('Port') is not None: + self.port = m.get('Port') + if m.get('SSHPublicKey') is not None: + self.sshpublic_key = m.get('SSHPublicKey') + return self + + +class ForwardInfoResponseConnectInfoInternet(TeaModel): + def __init__( + self, + endpoint: str = None, + port: str = None, + ): + self.endpoint = endpoint + self.port = port + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.endpoint is not None: + result['Endpoint'] = self.endpoint + if self.port is not None: + result['Port'] = self.port + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Endpoint') is not None: + self.endpoint = m.get('Endpoint') + if m.get('Port') is not None: + self.port = m.get('Port') + return self + + +class ForwardInfoResponseConnectInfoIntranet(TeaModel): + def __init__( + self, + endpoint: str = None, + port: str = None, + ): + self.endpoint = endpoint + self.port = port + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.endpoint is not None: + result['Endpoint'] = self.endpoint + if self.port is not None: + result['Port'] = self.port + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Endpoint') is not None: + self.endpoint = m.get('Endpoint') + if m.get('Port') is not None: + self.port = m.get('Port') + return self + + +class ForwardInfoResponseConnectInfo(TeaModel): + def __init__( + self, + internet: ForwardInfoResponseConnectInfoInternet = None, + intranet: ForwardInfoResponseConnectInfoIntranet = None, + message: str = None, + phase: str = None, + ): + self.internet = internet + self.intranet = intranet + self.message = message + self.phase = phase + + def validate(self): + if self.internet: + self.internet.validate() + if self.intranet: + self.intranet.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.internet is not None: + result['Internet'] = self.internet.to_map() + if self.intranet is not None: + result['Intranet'] = self.intranet.to_map() + if self.message is not None: + result['Message'] = self.message + if self.phase is not None: + result['Phase'] = self.phase + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Internet') is not None: + temp_model = ForwardInfoResponseConnectInfoInternet() + self.internet = temp_model.from_map(m['Internet']) + if m.get('Intranet') is not None: + temp_model = ForwardInfoResponseConnectInfoIntranet() + self.intranet = temp_model.from_map(m['Intranet']) + if m.get('Message') is not None: + self.message = m.get('Message') + if m.get('Phase') is not None: + self.phase = m.get('Phase') + return self + + +class ForwardInfoResponse(TeaModel): + def __init__( + self, + connect_info: ForwardInfoResponseConnectInfo = None, + container_name: str = None, + eip_allocation_id: str = None, + enable: bool = None, + nat_gateway_id: str = None, + port: str = None, + sshpublic_key: str = None, + ): + self.connect_info = connect_info + self.container_name = container_name + self.eip_allocation_id = eip_allocation_id + self.enable = enable + self.nat_gateway_id = nat_gateway_id + self.port = port + self.sshpublic_key = sshpublic_key + + def validate(self): + if self.connect_info: + self.connect_info.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.connect_info is not None: + result['ConnectInfo'] = self.connect_info.to_map() + if self.container_name is not None: + result['ContainerName'] = self.container_name + if self.eip_allocation_id is not None: + result['EipAllocationId'] = self.eip_allocation_id + if self.enable is not None: + result['Enable'] = self.enable + if self.nat_gateway_id is not None: + result['NatGatewayId'] = self.nat_gateway_id + if self.port is not None: + result['Port'] = self.port + if self.sshpublic_key is not None: + result['SSHPublicKey'] = self.sshpublic_key + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('ConnectInfo') is not None: + temp_model = ForwardInfoResponseConnectInfo() + self.connect_info = temp_model.from_map(m['ConnectInfo']) + if m.get('ContainerName') is not None: + self.container_name = m.get('ContainerName') + if m.get('EipAllocationId') is not None: + self.eip_allocation_id = m.get('EipAllocationId') + if m.get('Enable') is not None: + self.enable = m.get('Enable') + if m.get('NatGatewayId') is not None: + self.nat_gateway_id = m.get('NatGatewayId') + if m.get('Port') is not None: + self.port = m.get('Port') + if m.get('SSHPublicKey') is not None: + self.sshpublic_key = m.get('SSHPublicKey') + return self + + +class CreateIdleInstanceCullerRequest(TeaModel): + def __init__( + self, + cpu_percent_threshold: int = None, + gpu_percent_threshold: int = None, + max_idle_time_in_minutes: int = None, + ): + self.cpu_percent_threshold = cpu_percent_threshold + self.gpu_percent_threshold = gpu_percent_threshold + self.max_idle_time_in_minutes = max_idle_time_in_minutes + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.cpu_percent_threshold is not None: + result['CpuPercentThreshold'] = self.cpu_percent_threshold + if self.gpu_percent_threshold is not None: + result['GpuPercentThreshold'] = self.gpu_percent_threshold + if self.max_idle_time_in_minutes is not None: + result['MaxIdleTimeInMinutes'] = self.max_idle_time_in_minutes + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('CpuPercentThreshold') is not None: + self.cpu_percent_threshold = m.get('CpuPercentThreshold') + if m.get('GpuPercentThreshold') is not None: + self.gpu_percent_threshold = m.get('GpuPercentThreshold') + if m.get('MaxIdleTimeInMinutes') is not None: + self.max_idle_time_in_minutes = m.get('MaxIdleTimeInMinutes') + return self + + +class CreateIdleInstanceCullerResponseBody(TeaModel): + def __init__( + self, + code: str = None, + instance_id: str = None, + message: str = None, + request_id: str = None, + success: bool = None, + ): + self.code = code + self.instance_id = instance_id + self.message = message + self.request_id = request_id + self.success = success + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.code is not None: + result['Code'] = self.code + if self.instance_id is not None: + result['InstanceId'] = self.instance_id + if self.message is not None: + result['Message'] = self.message + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.success is not None: + result['Success'] = self.success + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Code') is not None: + self.code = m.get('Code') + if m.get('InstanceId') is not None: + self.instance_id = m.get('InstanceId') + if m.get('Message') is not None: + self.message = m.get('Message') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('Success') is not None: + self.success = m.get('Success') + return self + + +class CreateIdleInstanceCullerResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: CreateIdleInstanceCullerResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = CreateIdleInstanceCullerResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class CreateInstanceRequestCloudDisksStatus(TeaModel): + def __init__( + self, + available: int = None, + capacity: int = None, + usage: int = None, + ): + self.available = available + self.capacity = capacity + self.usage = usage + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.available is not None: + result['Available'] = self.available + if self.capacity is not None: + result['Capacity'] = self.capacity + if self.usage is not None: + result['Usage'] = self.usage + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Available') is not None: + self.available = m.get('Available') + if m.get('Capacity') is not None: + self.capacity = m.get('Capacity') + if m.get('Usage') is not None: + self.usage = m.get('Usage') + return self + + +class CreateInstanceRequestCloudDisks(TeaModel): + def __init__( + self, + capacity: str = None, + mount_path: str = None, + path: str = None, + status: CreateInstanceRequestCloudDisksStatus = None, + sub_type: str = None, + ): + self.capacity = capacity + self.mount_path = mount_path + self.path = path + self.status = status + self.sub_type = sub_type + + def validate(self): + if self.status: + self.status.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.capacity is not None: + result['Capacity'] = self.capacity + if self.mount_path is not None: + result['MountPath'] = self.mount_path + if self.path is not None: + result['Path'] = self.path + if self.status is not None: + result['Status'] = self.status.to_map() + if self.sub_type is not None: + result['SubType'] = self.sub_type + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Capacity') is not None: + self.capacity = m.get('Capacity') + if m.get('MountPath') is not None: + self.mount_path = m.get('MountPath') + if m.get('Path') is not None: + self.path = m.get('Path') + if m.get('Status') is not None: + temp_model = CreateInstanceRequestCloudDisksStatus() + self.status = temp_model.from_map(m['Status']) + if m.get('SubType') is not None: + self.sub_type = m.get('SubType') + return self + + +class CreateInstanceRequestDatasets(TeaModel): + def __init__( + self, + dataset_id: str = None, + mount_path: str = None, + ): + self.dataset_id = dataset_id + self.mount_path = mount_path + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.dataset_id is not None: + result['DatasetId'] = self.dataset_id + if self.mount_path is not None: + result['MountPath'] = self.mount_path + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('DatasetId') is not None: + self.dataset_id = m.get('DatasetId') + if m.get('MountPath') is not None: + self.mount_path = m.get('MountPath') + return self + + +class CreateInstanceRequestLabels(TeaModel): + def __init__( + self, + key: str = None, + value: str = None, + ): + self.key = key + self.value = value + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.key is not None: + result['Key'] = self.key + if self.value is not None: + result['Value'] = self.value + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Key') is not None: + self.key = m.get('Key') + if m.get('Value') is not None: + self.value = m.get('Value') + return self + + +class CreateInstanceRequestRequestedResource(TeaModel): + def __init__( + self, + cpu: str = None, + gpu: str = None, + gputype: str = None, + memory: str = None, + shared_memory: str = None, + ): + self.cpu = cpu + self.gpu = gpu + self.gputype = gputype + self.memory = memory + self.shared_memory = shared_memory + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.cpu is not None: + result['CPU'] = self.cpu + if self.gpu is not None: + result['GPU'] = self.gpu + if self.gputype is not None: + result['GPUType'] = self.gputype + if self.memory is not None: + result['Memory'] = self.memory + if self.shared_memory is not None: + result['SharedMemory'] = self.shared_memory + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('CPU') is not None: + self.cpu = m.get('CPU') + if m.get('GPU') is not None: + self.gpu = m.get('GPU') + if m.get('GPUType') is not None: + self.gputype = m.get('GPUType') + if m.get('Memory') is not None: + self.memory = m.get('Memory') + if m.get('SharedMemory') is not None: + self.shared_memory = m.get('SharedMemory') + return self + + +class CreateInstanceRequestUserVpc(TeaModel): + def __init__( + self, + default_route: str = None, + extended_cidrs: List[str] = None, + forward_infos: List[ForwardInfo] = None, + security_group_id: str = None, + v_switch_id: str = None, + vpc_id: str = None, + ): + self.default_route = default_route + self.extended_cidrs = extended_cidrs + self.forward_infos = forward_infos + self.security_group_id = security_group_id + self.v_switch_id = v_switch_id + self.vpc_id = vpc_id + + def validate(self): + if self.forward_infos: + for k in self.forward_infos: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.default_route is not None: + result['DefaultRoute'] = self.default_route + if self.extended_cidrs is not None: + result['ExtendedCIDRs'] = self.extended_cidrs + result['ForwardInfos'] = [] + if self.forward_infos is not None: + for k in self.forward_infos: + result['ForwardInfos'].append(k.to_map() if k else None) + if self.security_group_id is not None: + result['SecurityGroupId'] = self.security_group_id + if self.v_switch_id is not None: + result['VSwitchId'] = self.v_switch_id + if self.vpc_id is not None: + result['VpcId'] = self.vpc_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('DefaultRoute') is not None: + self.default_route = m.get('DefaultRoute') + if m.get('ExtendedCIDRs') is not None: + self.extended_cidrs = m.get('ExtendedCIDRs') + self.forward_infos = [] + if m.get('ForwardInfos') is not None: + for k in m.get('ForwardInfos'): + temp_model = ForwardInfo() + self.forward_infos.append(temp_model.from_map(k)) + if m.get('SecurityGroupId') is not None: + self.security_group_id = m.get('SecurityGroupId') + if m.get('VSwitchId') is not None: + self.v_switch_id = m.get('VSwitchId') + if m.get('VpcId') is not None: + self.vpc_id = m.get('VpcId') + return self + + +class CreateInstanceRequest(TeaModel): + def __init__( + self, + accessibility: str = None, + cloud_disks: List[CreateInstanceRequestCloudDisks] = None, + datasets: List[CreateInstanceRequestDatasets] = None, + driver: str = None, + ecs_spec: str = None, + environment_variables: Dict[str, str] = None, + image_id: str = None, + image_url: str = None, + instance_name: str = None, + labels: List[CreateInstanceRequestLabels] = None, + priority: int = None, + requested_resource: CreateInstanceRequestRequestedResource = None, + resource_id: str = None, + user_id: str = None, + user_vpc: CreateInstanceRequestUserVpc = None, + workspace_id: str = None, + workspace_source: str = None, + ): + self.accessibility = accessibility + self.cloud_disks = cloud_disks + self.datasets = datasets + self.driver = driver + self.ecs_spec = ecs_spec + self.environment_variables = environment_variables + self.image_id = image_id + self.image_url = image_url + self.instance_name = instance_name + self.labels = labels + self.priority = priority + self.requested_resource = requested_resource + self.resource_id = resource_id + self.user_id = user_id + self.user_vpc = user_vpc + self.workspace_id = workspace_id + self.workspace_source = workspace_source + + def validate(self): + if self.cloud_disks: + for k in self.cloud_disks: + if k: + k.validate() + if self.datasets: + for k in self.datasets: + if k: + k.validate() + if self.labels: + for k in self.labels: + if k: + k.validate() + if self.requested_resource: + self.requested_resource.validate() + if self.user_vpc: + self.user_vpc.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.accessibility is not None: + result['Accessibility'] = self.accessibility + result['CloudDisks'] = [] + if self.cloud_disks is not None: + for k in self.cloud_disks: + result['CloudDisks'].append(k.to_map() if k else None) + result['Datasets'] = [] + if self.datasets is not None: + for k in self.datasets: + result['Datasets'].append(k.to_map() if k else None) + if self.driver is not None: + result['Driver'] = self.driver + if self.ecs_spec is not None: + result['EcsSpec'] = self.ecs_spec + if self.environment_variables is not None: + result['EnvironmentVariables'] = self.environment_variables + if self.image_id is not None: + result['ImageId'] = self.image_id + if self.image_url is not None: + result['ImageUrl'] = self.image_url + if self.instance_name is not None: + result['InstanceName'] = self.instance_name + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + if self.priority is not None: + result['Priority'] = self.priority + if self.requested_resource is not None: + result['RequestedResource'] = self.requested_resource.to_map() + if self.resource_id is not None: + result['ResourceId'] = self.resource_id + if self.user_id is not None: + result['UserId'] = self.user_id + if self.user_vpc is not None: + result['UserVpc'] = self.user_vpc.to_map() + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id + if self.workspace_source is not None: + result['WorkspaceSource'] = self.workspace_source + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Accessibility') is not None: + self.accessibility = m.get('Accessibility') + self.cloud_disks = [] + if m.get('CloudDisks') is not None: + for k in m.get('CloudDisks'): + temp_model = CreateInstanceRequestCloudDisks() + self.cloud_disks.append(temp_model.from_map(k)) + self.datasets = [] + if m.get('Datasets') is not None: + for k in m.get('Datasets'): + temp_model = CreateInstanceRequestDatasets() + self.datasets.append(temp_model.from_map(k)) + if m.get('Driver') is not None: + self.driver = m.get('Driver') + if m.get('EcsSpec') is not None: + self.ecs_spec = m.get('EcsSpec') + if m.get('EnvironmentVariables') is not None: + self.environment_variables = m.get('EnvironmentVariables') + if m.get('ImageId') is not None: + self.image_id = m.get('ImageId') + if m.get('ImageUrl') is not None: + self.image_url = m.get('ImageUrl') + if m.get('InstanceName') is not None: + self.instance_name = m.get('InstanceName') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = CreateInstanceRequestLabels() + self.labels.append(temp_model.from_map(k)) + if m.get('Priority') is not None: + self.priority = m.get('Priority') + if m.get('RequestedResource') is not None: + temp_model = CreateInstanceRequestRequestedResource() + self.requested_resource = temp_model.from_map(m['RequestedResource']) + if m.get('ResourceId') is not None: + self.resource_id = m.get('ResourceId') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + if m.get('UserVpc') is not None: + temp_model = CreateInstanceRequestUserVpc() + self.user_vpc = temp_model.from_map(m['UserVpc']) + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') + if m.get('WorkspaceSource') is not None: + self.workspace_source = m.get('WorkspaceSource') + return self + + +class CreateInstanceResponseBody(TeaModel): + def __init__( + self, + code: str = None, + http_status_code: int = None, + instance_id: str = None, + message: str = None, + request_id: str = None, + success: bool = None, + ): + self.code = code + self.http_status_code = http_status_code + self.instance_id = instance_id + self.message = message + self.request_id = request_id + self.success = success + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.code is not None: + result['Code'] = self.code + if self.http_status_code is not None: + result['HttpStatusCode'] = self.http_status_code + if self.instance_id is not None: + result['InstanceId'] = self.instance_id + if self.message is not None: + result['Message'] = self.message + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.success is not None: + result['Success'] = self.success + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Code') is not None: + self.code = m.get('Code') + if m.get('HttpStatusCode') is not None: + self.http_status_code = m.get('HttpStatusCode') + if m.get('InstanceId') is not None: + self.instance_id = m.get('InstanceId') + if m.get('Message') is not None: + self.message = m.get('Message') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('Success') is not None: + self.success = m.get('Success') + return self + + +class CreateInstanceResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: CreateInstanceResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = CreateInstanceResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class CreateInstanceShutdownTimerRequest(TeaModel): + def __init__( + self, + due_time: str = None, + remaining_time_in_ms: int = None, + ): + self.due_time = due_time + self.remaining_time_in_ms = remaining_time_in_ms + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.due_time is not None: + result['DueTime'] = self.due_time + if self.remaining_time_in_ms is not None: + result['RemainingTimeInMs'] = self.remaining_time_in_ms + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('DueTime') is not None: + self.due_time = m.get('DueTime') + if m.get('RemainingTimeInMs') is not None: + self.remaining_time_in_ms = m.get('RemainingTimeInMs') + return self + + +class CreateInstanceShutdownTimerResponseBody(TeaModel): + def __init__( + self, + code: str = None, + http_status_code: int = None, + instance_id: str = None, + message: str = None, + request_id: str = None, + success: bool = None, + ): + self.code = code + self.http_status_code = http_status_code + self.instance_id = instance_id + self.message = message + self.request_id = request_id + self.success = success + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.code is not None: + result['Code'] = self.code + if self.http_status_code is not None: + result['HttpStatusCode'] = self.http_status_code + if self.instance_id is not None: + result['InstanceId'] = self.instance_id + if self.message is not None: + result['Message'] = self.message + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.success is not None: + result['Success'] = self.success + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Code') is not None: + self.code = m.get('Code') + if m.get('HttpStatusCode') is not None: + self.http_status_code = m.get('HttpStatusCode') + if m.get('InstanceId') is not None: + self.instance_id = m.get('InstanceId') + if m.get('Message') is not None: + self.message = m.get('Message') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('Success') is not None: + self.success = m.get('Success') + return self + + +class CreateInstanceShutdownTimerResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: CreateInstanceShutdownTimerResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = CreateInstanceShutdownTimerResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class CreateInstanceSnapshotRequestLabels(TeaModel): + def __init__( + self, + key: str = None, + value: str = None, + ): + self.key = key + self.value = value + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.key is not None: + result['Key'] = self.key + if self.value is not None: + result['Value'] = self.value + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Key') is not None: + self.key = m.get('Key') + if m.get('Value') is not None: + self.value = m.get('Value') + return self + + +class CreateInstanceSnapshotRequest(TeaModel): + def __init__( + self, + exclude_paths: List[str] = None, + image_url: str = None, + labels: List[CreateInstanceSnapshotRequestLabels] = None, + overwrite: bool = None, + snapshot_description: str = None, + snapshot_name: str = None, + ): + self.exclude_paths = exclude_paths + self.image_url = image_url + self.labels = labels + self.overwrite = overwrite + self.snapshot_description = snapshot_description + self.snapshot_name = snapshot_name + + def validate(self): + if self.labels: + for k in self.labels: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.exclude_paths is not None: + result['ExcludePaths'] = self.exclude_paths + if self.image_url is not None: + result['ImageUrl'] = self.image_url + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + if self.overwrite is not None: + result['Overwrite'] = self.overwrite + if self.snapshot_description is not None: + result['SnapshotDescription'] = self.snapshot_description + if self.snapshot_name is not None: + result['SnapshotName'] = self.snapshot_name + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('ExcludePaths') is not None: + self.exclude_paths = m.get('ExcludePaths') + if m.get('ImageUrl') is not None: + self.image_url = m.get('ImageUrl') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = CreateInstanceSnapshotRequestLabels() + self.labels.append(temp_model.from_map(k)) + if m.get('Overwrite') is not None: + self.overwrite = m.get('Overwrite') + if m.get('SnapshotDescription') is not None: + self.snapshot_description = m.get('SnapshotDescription') + if m.get('SnapshotName') is not None: + self.snapshot_name = m.get('SnapshotName') + return self + + +class CreateInstanceSnapshotResponseBody(TeaModel): + def __init__( + self, + code: str = None, + http_status_code: int = None, + instance_id: str = None, + message: str = None, + request_id: str = None, + snapshot_id: str = None, + success: bool = None, + ): + self.code = code + self.http_status_code = http_status_code + self.instance_id = instance_id + self.message = message + self.request_id = request_id + self.snapshot_id = snapshot_id + self.success = success + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.code is not None: + result['Code'] = self.code + if self.http_status_code is not None: + result['HttpStatusCode'] = self.http_status_code + if self.instance_id is not None: + result['InstanceId'] = self.instance_id + if self.message is not None: + result['Message'] = self.message + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.snapshot_id is not None: + result['SnapshotId'] = self.snapshot_id + if self.success is not None: + result['Success'] = self.success + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Code') is not None: + self.code = m.get('Code') + if m.get('HttpStatusCode') is not None: + self.http_status_code = m.get('HttpStatusCode') + if m.get('InstanceId') is not None: + self.instance_id = m.get('InstanceId') + if m.get('Message') is not None: + self.message = m.get('Message') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('SnapshotId') is not None: + self.snapshot_id = m.get('SnapshotId') + if m.get('Success') is not None: + self.success = m.get('Success') + return self + + +class CreateInstanceSnapshotResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: CreateInstanceSnapshotResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = CreateInstanceSnapshotResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class DeleteIdleInstanceCullerResponseBody(TeaModel): + def __init__( + self, + code: str = None, + instance_id: str = None, + message: str = None, + request_id: str = None, + success: bool = None, + ): + self.code = code + self.instance_id = instance_id + self.message = message + self.request_id = request_id + self.success = success + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.code is not None: + result['Code'] = self.code + if self.instance_id is not None: + result['InstanceId'] = self.instance_id + if self.message is not None: + result['Message'] = self.message + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.success is not None: + result['Success'] = self.success + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Code') is not None: + self.code = m.get('Code') + if m.get('InstanceId') is not None: + self.instance_id = m.get('InstanceId') + if m.get('Message') is not None: + self.message = m.get('Message') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('Success') is not None: + self.success = m.get('Success') + return self + + +class DeleteIdleInstanceCullerResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: DeleteIdleInstanceCullerResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = DeleteIdleInstanceCullerResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class DeleteInstanceResponseBody(TeaModel): + def __init__( + self, + code: str = None, + http_status_code: int = None, + instance_id: str = None, + message: str = None, + request_id: str = None, + success: bool = None, + ): + self.code = code + self.http_status_code = http_status_code + self.instance_id = instance_id + self.message = message + self.request_id = request_id + self.success = success + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.code is not None: + result['Code'] = self.code + if self.http_status_code is not None: + result['HttpStatusCode'] = self.http_status_code + if self.instance_id is not None: + result['InstanceId'] = self.instance_id + if self.message is not None: + result['Message'] = self.message + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.success is not None: + result['Success'] = self.success + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Code') is not None: + self.code = m.get('Code') + if m.get('HttpStatusCode') is not None: + self.http_status_code = m.get('HttpStatusCode') + if m.get('InstanceId') is not None: + self.instance_id = m.get('InstanceId') + if m.get('Message') is not None: + self.message = m.get('Message') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('Success') is not None: + self.success = m.get('Success') + return self + + +class DeleteInstanceResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: DeleteInstanceResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = DeleteInstanceResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class DeleteInstanceShutdownTimerResponseBody(TeaModel): + def __init__( + self, + code: str = None, + http_status_code: int = None, + instance_id: str = None, + message: str = None, + request_id: str = None, + success: bool = None, + ): + self.code = code + self.http_status_code = http_status_code + self.instance_id = instance_id + self.message = message + self.request_id = request_id + self.success = success + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.code is not None: + result['Code'] = self.code + if self.http_status_code is not None: + result['HttpStatusCode'] = self.http_status_code + if self.instance_id is not None: + result['InstanceId'] = self.instance_id + if self.message is not None: + result['Message'] = self.message + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.success is not None: + result['Success'] = self.success + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Code') is not None: + self.code = m.get('Code') + if m.get('HttpStatusCode') is not None: + self.http_status_code = m.get('HttpStatusCode') + if m.get('InstanceId') is not None: + self.instance_id = m.get('InstanceId') + if m.get('Message') is not None: + self.message = m.get('Message') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('Success') is not None: + self.success = m.get('Success') + return self + + +class DeleteInstanceShutdownTimerResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: DeleteInstanceShutdownTimerResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = DeleteInstanceShutdownTimerResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class DeleteInstanceSnapshotResponseBody(TeaModel): + def __init__( + self, + code: str = None, + http_status_code: int = None, + instance_id: str = None, + message: str = None, + request_id: str = None, + snapshot_id: str = None, + success: bool = None, + ): + self.code = code + self.http_status_code = http_status_code + self.instance_id = instance_id + self.message = message + self.request_id = request_id + self.snapshot_id = snapshot_id + self.success = success + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.code is not None: + result['Code'] = self.code + if self.http_status_code is not None: + result['HttpStatusCode'] = self.http_status_code + if self.instance_id is not None: + result['InstanceId'] = self.instance_id + if self.message is not None: + result['Message'] = self.message + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.snapshot_id is not None: + result['SnapshotId'] = self.snapshot_id + if self.success is not None: + result['Success'] = self.success + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Code') is not None: + self.code = m.get('Code') + if m.get('HttpStatusCode') is not None: + self.http_status_code = m.get('HttpStatusCode') + if m.get('InstanceId') is not None: + self.instance_id = m.get('InstanceId') + if m.get('Message') is not None: + self.message = m.get('Message') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('SnapshotId') is not None: + self.snapshot_id = m.get('SnapshotId') + if m.get('Success') is not None: + self.success = m.get('Success') + return self + + +class DeleteInstanceSnapshotResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: DeleteInstanceSnapshotResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = DeleteInstanceSnapshotResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetIdleInstanceCullerResponseBody(TeaModel): + def __init__( + self, + code: str = None, + cpu_percent_threshold: int = None, + gpu_percent_threshold: int = None, + idle_time_in_minutes: int = None, + instance_id: str = None, + max_idle_time_in_minutes: int = None, + message: str = None, + request_id: str = None, + success: bool = None, + ): + self.code = code + self.cpu_percent_threshold = cpu_percent_threshold + self.gpu_percent_threshold = gpu_percent_threshold + self.idle_time_in_minutes = idle_time_in_minutes + self.instance_id = instance_id + self.max_idle_time_in_minutes = max_idle_time_in_minutes + self.message = message + self.request_id = request_id + self.success = success + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.code is not None: + result['Code'] = self.code + if self.cpu_percent_threshold is not None: + result['CpuPercentThreshold'] = self.cpu_percent_threshold + if self.gpu_percent_threshold is not None: + result['GpuPercentThreshold'] = self.gpu_percent_threshold + if self.idle_time_in_minutes is not None: + result['IdleTimeInMinutes'] = self.idle_time_in_minutes + if self.instance_id is not None: + result['InstanceId'] = self.instance_id + if self.max_idle_time_in_minutes is not None: + result['MaxIdleTimeInMinutes'] = self.max_idle_time_in_minutes + if self.message is not None: + result['Message'] = self.message + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.success is not None: + result['Success'] = self.success + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Code') is not None: + self.code = m.get('Code') + if m.get('CpuPercentThreshold') is not None: + self.cpu_percent_threshold = m.get('CpuPercentThreshold') + if m.get('GpuPercentThreshold') is not None: + self.gpu_percent_threshold = m.get('GpuPercentThreshold') + if m.get('IdleTimeInMinutes') is not None: + self.idle_time_in_minutes = m.get('IdleTimeInMinutes') + if m.get('InstanceId') is not None: + self.instance_id = m.get('InstanceId') + if m.get('MaxIdleTimeInMinutes') is not None: + self.max_idle_time_in_minutes = m.get('MaxIdleTimeInMinutes') + if m.get('Message') is not None: + self.message = m.get('Message') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('Success') is not None: + self.success = m.get('Success') + return self + + +class GetIdleInstanceCullerResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetIdleInstanceCullerResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetIdleInstanceCullerResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetInstanceResponseBodyCloudDisks(TeaModel): + def __init__( + self, + capacity: str = None, + mount_path: str = None, + path: str = None, + sub_type: str = None, + ): + self.capacity = capacity + self.mount_path = mount_path + self.path = path + self.sub_type = sub_type + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.capacity is not None: + result['Capacity'] = self.capacity + if self.mount_path is not None: + result['MountPath'] = self.mount_path + if self.path is not None: + result['Path'] = self.path + if self.sub_type is not None: + result['SubType'] = self.sub_type + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Capacity') is not None: + self.capacity = m.get('Capacity') + if m.get('MountPath') is not None: + self.mount_path = m.get('MountPath') + if m.get('Path') is not None: + self.path = m.get('Path') + if m.get('SubType') is not None: + self.sub_type = m.get('SubType') + return self + + +class GetInstanceResponseBodyDatasets(TeaModel): + def __init__( + self, + dataset_id: str = None, + mount_path: str = None, + ): + self.dataset_id = dataset_id + self.mount_path = mount_path + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.dataset_id is not None: + result['DatasetId'] = self.dataset_id + if self.mount_path is not None: + result['MountPath'] = self.mount_path + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('DatasetId') is not None: + self.dataset_id = m.get('DatasetId') + if m.get('MountPath') is not None: + self.mount_path = m.get('MountPath') + return self + + +class GetInstanceResponseBodyIdleInstanceCuller(TeaModel): + def __init__( + self, + cpu_percent_threshold: int = None, + gpu_percent_threshold: int = None, + idle_time_in_minutes: int = None, + instance_id: str = None, + max_idle_time_in_minutes: int = None, + ): + self.cpu_percent_threshold = cpu_percent_threshold + self.gpu_percent_threshold = gpu_percent_threshold + self.idle_time_in_minutes = idle_time_in_minutes + self.instance_id = instance_id + self.max_idle_time_in_minutes = max_idle_time_in_minutes + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.cpu_percent_threshold is not None: + result['CpuPercentThreshold'] = self.cpu_percent_threshold + if self.gpu_percent_threshold is not None: + result['GpuPercentThreshold'] = self.gpu_percent_threshold + if self.idle_time_in_minutes is not None: + result['IdleTimeInMinutes'] = self.idle_time_in_minutes + if self.instance_id is not None: + result['InstanceId'] = self.instance_id + if self.max_idle_time_in_minutes is not None: + result['MaxIdleTimeInMinutes'] = self.max_idle_time_in_minutes + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('CpuPercentThreshold') is not None: + self.cpu_percent_threshold = m.get('CpuPercentThreshold') + if m.get('GpuPercentThreshold') is not None: + self.gpu_percent_threshold = m.get('GpuPercentThreshold') + if m.get('IdleTimeInMinutes') is not None: + self.idle_time_in_minutes = m.get('IdleTimeInMinutes') + if m.get('InstanceId') is not None: + self.instance_id = m.get('InstanceId') + if m.get('MaxIdleTimeInMinutes') is not None: + self.max_idle_time_in_minutes = m.get('MaxIdleTimeInMinutes') + return self + + +class GetInstanceResponseBodyInstanceShutdownTimer(TeaModel): + def __init__( + self, + due_time: str = None, + gmt_create_time: str = None, + gmt_modified_time: str = None, + instance_id: str = None, + remaining_time_in_ms: int = None, + ): + self.due_time = due_time + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time + self.instance_id = instance_id + self.remaining_time_in_ms = remaining_time_in_ms + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.due_time is not None: + result['DueTime'] = self.due_time + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + if self.instance_id is not None: + result['InstanceId'] = self.instance_id + if self.remaining_time_in_ms is not None: + result['RemainingTimeInMs'] = self.remaining_time_in_ms + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('DueTime') is not None: + self.due_time = m.get('DueTime') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + if m.get('InstanceId') is not None: + self.instance_id = m.get('InstanceId') + if m.get('RemainingTimeInMs') is not None: + self.remaining_time_in_ms = m.get('RemainingTimeInMs') + return self + + +class GetInstanceResponseBodyInstanceSnapshotList(TeaModel): + def __init__( + self, + gmt_create_time: str = None, + gmt_modified_time: str = None, + image_id: str = None, + image_name: str = None, + image_url: str = None, + reason_code: str = None, + reason_message: str = None, + repository_url: str = None, + status: str = None, + ): + # 快照创建时间 + self.gmt_create_time = gmt_create_time + # 快照修改时间 + self.gmt_modified_time = gmt_modified_time + # 镜像Id + self.image_id = image_id + # 镜像名称 + self.image_name = image_name + # 镜像Url + self.image_url = image_url + # 实例快照错误代码 + self.reason_code = reason_code + # 实例快照错误消息 + self.reason_message = reason_message + # 镜像仓库Url + self.repository_url = repository_url + # 实例快照状态 + self.status = status + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + if self.image_id is not None: + result['ImageId'] = self.image_id + if self.image_name is not None: + result['ImageName'] = self.image_name + if self.image_url is not None: + result['ImageUrl'] = self.image_url + if self.reason_code is not None: + result['ReasonCode'] = self.reason_code + if self.reason_message is not None: + result['ReasonMessage'] = self.reason_message + if self.repository_url is not None: + result['RepositoryUrl'] = self.repository_url + if self.status is not None: + result['Status'] = self.status + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + if m.get('ImageId') is not None: + self.image_id = m.get('ImageId') + if m.get('ImageName') is not None: + self.image_name = m.get('ImageName') + if m.get('ImageUrl') is not None: + self.image_url = m.get('ImageUrl') + if m.get('ReasonCode') is not None: + self.reason_code = m.get('ReasonCode') + if m.get('ReasonMessage') is not None: + self.reason_message = m.get('ReasonMessage') + if m.get('RepositoryUrl') is not None: + self.repository_url = m.get('RepositoryUrl') + if m.get('Status') is not None: + self.status = m.get('Status') + return self + + +class GetInstanceResponseBodyLabels(TeaModel): + def __init__( + self, + key: str = None, + value: str = None, + ): + self.key = key + self.value = value + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.key is not None: + result['Key'] = self.key + if self.value is not None: + result['Value'] = self.value + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Key') is not None: + self.key = m.get('Key') + if m.get('Value') is not None: + self.value = m.get('Value') + return self + + +class GetInstanceResponseBodyLatestSnapshot(TeaModel): + def __init__( + self, + gmt_create_time: str = None, + gmt_modified_time: str = None, + image_id: str = None, + image_name: str = None, + image_url: str = None, + reason_code: str = None, + reason_message: str = None, + repository_url: str = None, + status: str = None, + ): + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time + self.image_id = image_id + self.image_name = image_name + self.image_url = image_url + # 实例快照错误代码 + self.reason_code = reason_code + # 实例快照错误消息 + self.reason_message = reason_message + self.repository_url = repository_url + # 实例快照状态 + self.status = status + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + if self.image_id is not None: + result['ImageId'] = self.image_id + if self.image_name is not None: + result['ImageName'] = self.image_name + if self.image_url is not None: + result['ImageUrl'] = self.image_url + if self.reason_code is not None: + result['ReasonCode'] = self.reason_code + if self.reason_message is not None: + result['ReasonMessage'] = self.reason_message + if self.repository_url is not None: + result['RepositoryUrl'] = self.repository_url + if self.status is not None: + result['Status'] = self.status + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + if m.get('ImageId') is not None: + self.image_id = m.get('ImageId') + if m.get('ImageName') is not None: + self.image_name = m.get('ImageName') + if m.get('ImageUrl') is not None: + self.image_url = m.get('ImageUrl') + if m.get('ReasonCode') is not None: + self.reason_code = m.get('ReasonCode') + if m.get('ReasonMessage') is not None: + self.reason_message = m.get('ReasonMessage') + if m.get('RepositoryUrl') is not None: + self.repository_url = m.get('RepositoryUrl') + if m.get('Status') is not None: + self.status = m.get('Status') + return self + + +class GetInstanceResponseBodyNodeErrorRecovery(TeaModel): + def __init__( + self, + auto_switch_countdown_seconds: int = None, + enable_auto_switch_on_node_error: bool = None, + has_node_error: bool = None, + ): + self.auto_switch_countdown_seconds = auto_switch_countdown_seconds + self.enable_auto_switch_on_node_error = enable_auto_switch_on_node_error + self.has_node_error = has_node_error + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.auto_switch_countdown_seconds is not None: + result['autoSwitchCountdownSeconds'] = self.auto_switch_countdown_seconds + if self.enable_auto_switch_on_node_error is not None: + result['enableAutoSwitchOnNodeError'] = self.enable_auto_switch_on_node_error + if self.has_node_error is not None: + result['hasNodeError'] = self.has_node_error + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('autoSwitchCountdownSeconds') is not None: + self.auto_switch_countdown_seconds = m.get('autoSwitchCountdownSeconds') + if m.get('enableAutoSwitchOnNodeError') is not None: + self.enable_auto_switch_on_node_error = m.get('enableAutoSwitchOnNodeError') + if m.get('hasNodeError') is not None: + self.has_node_error = m.get('hasNodeError') + return self + + +class GetInstanceResponseBodyRequestedResource(TeaModel): + def __init__( + self, + cpu: str = None, + gpu: str = None, + gputype: str = None, + memory: str = None, + shared_memory: str = None, + ): + self.cpu = cpu + self.gpu = gpu + self.gputype = gputype + self.memory = memory + self.shared_memory = shared_memory + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.cpu is not None: + result['CPU'] = self.cpu + if self.gpu is not None: + result['GPU'] = self.gpu + if self.gputype is not None: + result['GPUType'] = self.gputype + if self.memory is not None: + result['Memory'] = self.memory + if self.shared_memory is not None: + result['SharedMemory'] = self.shared_memory + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('CPU') is not None: + self.cpu = m.get('CPU') + if m.get('GPU') is not None: + self.gpu = m.get('GPU') + if m.get('GPUType') is not None: + self.gputype = m.get('GPUType') + if m.get('Memory') is not None: + self.memory = m.get('Memory') + if m.get('SharedMemory') is not None: + self.shared_memory = m.get('SharedMemory') + return self + + +class GetInstanceResponseBodyUserVpc(TeaModel): + def __init__( + self, + default_route: str = None, + extended_cidrs: List[str] = None, + forward_infos: List[ForwardInfoResponse] = None, + security_group_id: str = None, + v_switch_id: str = None, + vpc_id: str = None, + ): + self.default_route = default_route + self.extended_cidrs = extended_cidrs + self.forward_infos = forward_infos + self.security_group_id = security_group_id + self.v_switch_id = v_switch_id + # Vpc Id。 + self.vpc_id = vpc_id + + def validate(self): + if self.forward_infos: + for k in self.forward_infos: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.default_route is not None: + result['DefaultRoute'] = self.default_route + if self.extended_cidrs is not None: + result['ExtendedCIDRs'] = self.extended_cidrs + result['ForwardInfos'] = [] + if self.forward_infos is not None: + for k in self.forward_infos: + result['ForwardInfos'].append(k.to_map() if k else None) + if self.security_group_id is not None: + result['SecurityGroupId'] = self.security_group_id + if self.v_switch_id is not None: + result['VSwitchId'] = self.v_switch_id + if self.vpc_id is not None: + result['VpcId'] = self.vpc_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('DefaultRoute') is not None: + self.default_route = m.get('DefaultRoute') + if m.get('ExtendedCIDRs') is not None: + self.extended_cidrs = m.get('ExtendedCIDRs') + self.forward_infos = [] + if m.get('ForwardInfos') is not None: + for k in m.get('ForwardInfos'): + temp_model = ForwardInfoResponse() + self.forward_infos.append(temp_model.from_map(k)) + if m.get('SecurityGroupId') is not None: + self.security_group_id = m.get('SecurityGroupId') + if m.get('VSwitchId') is not None: + self.v_switch_id = m.get('VSwitchId') + if m.get('VpcId') is not None: + self.vpc_id = m.get('VpcId') + return self + + +class GetInstanceResponseBody(TeaModel): + def __init__( + self, + accelerator_type: str = None, + accessibility: str = None, + accumulated_running_time_in_ms: int = None, + cloud_disks: List[GetInstanceResponseBodyCloudDisks] = None, + code: str = None, + datasets: List[GetInstanceResponseBodyDatasets] = None, + driver: str = None, + ecs_spec: str = None, + environment_variables: Dict[str, str] = None, + gmt_create_time: str = None, + gmt_modified_time: str = None, + http_status_code: int = None, + idle_instance_culler: GetInstanceResponseBodyIdleInstanceCuller = None, + image_id: str = None, + image_name: str = None, + image_url: str = None, + instance_id: str = None, + instance_name: str = None, + instance_shutdown_timer: GetInstanceResponseBodyInstanceShutdownTimer = None, + instance_snapshot_list: List[GetInstanceResponseBodyInstanceSnapshotList] = None, + instance_url: str = None, + jupyterlab_url: str = None, + labels: List[GetInstanceResponseBodyLabels] = None, + latest_snapshot: GetInstanceResponseBodyLatestSnapshot = None, + message: str = None, + node_error_recovery: GetInstanceResponseBodyNodeErrorRecovery = None, + payment_type: str = None, + priority: int = None, + reason_code: str = None, + reason_message: str = None, + request_id: str = None, + requested_resource: GetInstanceResponseBodyRequestedResource = None, + resource_id: str = None, + resource_name: str = None, + status: str = None, + success: bool = None, + terminal_url: str = None, + user_id: str = None, + user_name: str = None, + user_vpc: GetInstanceResponseBodyUserVpc = None, + web_ideurl: str = None, + workspace_id: str = None, + workspace_name: str = None, + workspace_source: str = None, + ): + self.accelerator_type = accelerator_type + self.accessibility = accessibility + self.accumulated_running_time_in_ms = accumulated_running_time_in_ms + self.cloud_disks = cloud_disks + self.code = code + self.datasets = datasets + self.driver = driver + self.ecs_spec = ecs_spec + self.environment_variables = environment_variables + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time + self.http_status_code = http_status_code + self.idle_instance_culler = idle_instance_culler + self.image_id = image_id + self.image_name = image_name + self.image_url = image_url + self.instance_id = instance_id + self.instance_name = instance_name + self.instance_shutdown_timer = instance_shutdown_timer + self.instance_snapshot_list = instance_snapshot_list + self.instance_url = instance_url + # Jupyterlab Url。 + self.jupyterlab_url = jupyterlab_url + self.labels = labels + self.latest_snapshot = latest_snapshot + self.message = message + self.node_error_recovery = node_error_recovery + self.payment_type = payment_type + self.priority = priority + self.reason_code = reason_code + self.reason_message = reason_message + self.request_id = request_id + self.requested_resource = requested_resource + self.resource_id = resource_id + self.resource_name = resource_name + self.status = status + self.success = success + self.terminal_url = terminal_url + self.user_id = user_id + self.user_name = user_name + self.user_vpc = user_vpc + # Web IDE url。 + self.web_ideurl = web_ideurl + self.workspace_id = workspace_id + self.workspace_name = workspace_name + self.workspace_source = workspace_source + + def validate(self): + if self.cloud_disks: + for k in self.cloud_disks: + if k: + k.validate() + if self.datasets: + for k in self.datasets: + if k: + k.validate() + if self.idle_instance_culler: + self.idle_instance_culler.validate() + if self.instance_shutdown_timer: + self.instance_shutdown_timer.validate() + if self.instance_snapshot_list: + for k in self.instance_snapshot_list: + if k: + k.validate() + if self.labels: + for k in self.labels: + if k: + k.validate() + if self.latest_snapshot: + self.latest_snapshot.validate() + if self.node_error_recovery: + self.node_error_recovery.validate() + if self.requested_resource: + self.requested_resource.validate() + if self.user_vpc: + self.user_vpc.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.accelerator_type is not None: + result['AcceleratorType'] = self.accelerator_type + if self.accessibility is not None: + result['Accessibility'] = self.accessibility + if self.accumulated_running_time_in_ms is not None: + result['AccumulatedRunningTimeInMs'] = self.accumulated_running_time_in_ms + result['CloudDisks'] = [] + if self.cloud_disks is not None: + for k in self.cloud_disks: + result['CloudDisks'].append(k.to_map() if k else None) + if self.code is not None: + result['Code'] = self.code + result['Datasets'] = [] + if self.datasets is not None: + for k in self.datasets: + result['Datasets'].append(k.to_map() if k else None) + if self.driver is not None: + result['Driver'] = self.driver + if self.ecs_spec is not None: + result['EcsSpec'] = self.ecs_spec + if self.environment_variables is not None: + result['EnvironmentVariables'] = self.environment_variables + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + if self.http_status_code is not None: + result['HttpStatusCode'] = self.http_status_code + if self.idle_instance_culler is not None: + result['IdleInstanceCuller'] = self.idle_instance_culler.to_map() + if self.image_id is not None: + result['ImageId'] = self.image_id + if self.image_name is not None: + result['ImageName'] = self.image_name + if self.image_url is not None: + result['ImageUrl'] = self.image_url + if self.instance_id is not None: + result['InstanceId'] = self.instance_id + if self.instance_name is not None: + result['InstanceName'] = self.instance_name + if self.instance_shutdown_timer is not None: + result['InstanceShutdownTimer'] = self.instance_shutdown_timer.to_map() + result['InstanceSnapshotList'] = [] + if self.instance_snapshot_list is not None: + for k in self.instance_snapshot_list: + result['InstanceSnapshotList'].append(k.to_map() if k else None) + if self.instance_url is not None: + result['InstanceUrl'] = self.instance_url + if self.jupyterlab_url is not None: + result['JupyterlabUrl'] = self.jupyterlab_url + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + if self.latest_snapshot is not None: + result['LatestSnapshot'] = self.latest_snapshot.to_map() + if self.message is not None: + result['Message'] = self.message + if self.node_error_recovery is not None: + result['NodeErrorRecovery'] = self.node_error_recovery.to_map() + if self.payment_type is not None: + result['PaymentType'] = self.payment_type + if self.priority is not None: + result['Priority'] = self.priority + if self.reason_code is not None: + result['ReasonCode'] = self.reason_code + if self.reason_message is not None: + result['ReasonMessage'] = self.reason_message + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.requested_resource is not None: + result['RequestedResource'] = self.requested_resource.to_map() + if self.resource_id is not None: + result['ResourceId'] = self.resource_id + if self.resource_name is not None: + result['ResourceName'] = self.resource_name + if self.status is not None: + result['Status'] = self.status + if self.success is not None: + result['Success'] = self.success + if self.terminal_url is not None: + result['TerminalUrl'] = self.terminal_url + if self.user_id is not None: + result['UserId'] = self.user_id + if self.user_name is not None: + result['UserName'] = self.user_name + if self.user_vpc is not None: + result['UserVpc'] = self.user_vpc.to_map() + if self.web_ideurl is not None: + result['WebIDEUrl'] = self.web_ideurl + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id + if self.workspace_name is not None: + result['WorkspaceName'] = self.workspace_name + if self.workspace_source is not None: + result['WorkspaceSource'] = self.workspace_source + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('AcceleratorType') is not None: + self.accelerator_type = m.get('AcceleratorType') + if m.get('Accessibility') is not None: + self.accessibility = m.get('Accessibility') + if m.get('AccumulatedRunningTimeInMs') is not None: + self.accumulated_running_time_in_ms = m.get('AccumulatedRunningTimeInMs') + self.cloud_disks = [] + if m.get('CloudDisks') is not None: + for k in m.get('CloudDisks'): + temp_model = GetInstanceResponseBodyCloudDisks() + self.cloud_disks.append(temp_model.from_map(k)) + if m.get('Code') is not None: + self.code = m.get('Code') + self.datasets = [] + if m.get('Datasets') is not None: + for k in m.get('Datasets'): + temp_model = GetInstanceResponseBodyDatasets() + self.datasets.append(temp_model.from_map(k)) + if m.get('Driver') is not None: + self.driver = m.get('Driver') + if m.get('EcsSpec') is not None: + self.ecs_spec = m.get('EcsSpec') + if m.get('EnvironmentVariables') is not None: + self.environment_variables = m.get('EnvironmentVariables') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + if m.get('HttpStatusCode') is not None: + self.http_status_code = m.get('HttpStatusCode') + if m.get('IdleInstanceCuller') is not None: + temp_model = GetInstanceResponseBodyIdleInstanceCuller() + self.idle_instance_culler = temp_model.from_map(m['IdleInstanceCuller']) + if m.get('ImageId') is not None: + self.image_id = m.get('ImageId') + if m.get('ImageName') is not None: + self.image_name = m.get('ImageName') + if m.get('ImageUrl') is not None: + self.image_url = m.get('ImageUrl') + if m.get('InstanceId') is not None: + self.instance_id = m.get('InstanceId') + if m.get('InstanceName') is not None: + self.instance_name = m.get('InstanceName') + if m.get('InstanceShutdownTimer') is not None: + temp_model = GetInstanceResponseBodyInstanceShutdownTimer() + self.instance_shutdown_timer = temp_model.from_map(m['InstanceShutdownTimer']) + self.instance_snapshot_list = [] + if m.get('InstanceSnapshotList') is not None: + for k in m.get('InstanceSnapshotList'): + temp_model = GetInstanceResponseBodyInstanceSnapshotList() + self.instance_snapshot_list.append(temp_model.from_map(k)) + if m.get('InstanceUrl') is not None: + self.instance_url = m.get('InstanceUrl') + if m.get('JupyterlabUrl') is not None: + self.jupyterlab_url = m.get('JupyterlabUrl') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = GetInstanceResponseBodyLabels() + self.labels.append(temp_model.from_map(k)) + if m.get('LatestSnapshot') is not None: + temp_model = GetInstanceResponseBodyLatestSnapshot() + self.latest_snapshot = temp_model.from_map(m['LatestSnapshot']) + if m.get('Message') is not None: + self.message = m.get('Message') + if m.get('NodeErrorRecovery') is not None: + temp_model = GetInstanceResponseBodyNodeErrorRecovery() + self.node_error_recovery = temp_model.from_map(m['NodeErrorRecovery']) + if m.get('PaymentType') is not None: + self.payment_type = m.get('PaymentType') + if m.get('Priority') is not None: + self.priority = m.get('Priority') + if m.get('ReasonCode') is not None: + self.reason_code = m.get('ReasonCode') + if m.get('ReasonMessage') is not None: + self.reason_message = m.get('ReasonMessage') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('RequestedResource') is not None: + temp_model = GetInstanceResponseBodyRequestedResource() + self.requested_resource = temp_model.from_map(m['RequestedResource']) + if m.get('ResourceId') is not None: + self.resource_id = m.get('ResourceId') + if m.get('ResourceName') is not None: + self.resource_name = m.get('ResourceName') + if m.get('Status') is not None: + self.status = m.get('Status') + if m.get('Success') is not None: + self.success = m.get('Success') + if m.get('TerminalUrl') is not None: + self.terminal_url = m.get('TerminalUrl') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + if m.get('UserName') is not None: + self.user_name = m.get('UserName') + if m.get('UserVpc') is not None: + temp_model = GetInstanceResponseBodyUserVpc() + self.user_vpc = temp_model.from_map(m['UserVpc']) + if m.get('WebIDEUrl') is not None: + self.web_ideurl = m.get('WebIDEUrl') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') + if m.get('WorkspaceName') is not None: + self.workspace_name = m.get('WorkspaceName') + if m.get('WorkspaceSource') is not None: + self.workspace_source = m.get('WorkspaceSource') + return self + + +class GetInstanceResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetInstanceResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetInstanceResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetInstanceEventsRequest(TeaModel): + def __init__( + self, + end_time: str = None, + max_events_num: int = None, + start_time: str = None, + ): + self.end_time = end_time + self.max_events_num = max_events_num + self.start_time = start_time + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.end_time is not None: + result['EndTime'] = self.end_time + if self.max_events_num is not None: + result['MaxEventsNum'] = self.max_events_num + if self.start_time is not None: + result['StartTime'] = self.start_time + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('EndTime') is not None: + self.end_time = m.get('EndTime') + if m.get('MaxEventsNum') is not None: + self.max_events_num = m.get('MaxEventsNum') + if m.get('StartTime') is not None: + self.start_time = m.get('StartTime') + return self + + +class GetInstanceEventsResponseBody(TeaModel): + def __init__( + self, + code: str = None, + events: List[str] = None, + http_status_code: int = None, + instance_id: str = None, + message: str = None, + request_id: str = None, + success: bool = None, + ): + self.code = code + self.events = events + self.http_status_code = http_status_code + self.instance_id = instance_id + self.message = message + self.request_id = request_id + self.success = success + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.code is not None: + result['Code'] = self.code + if self.events is not None: + result['Events'] = self.events + if self.http_status_code is not None: + result['HttpStatusCode'] = self.http_status_code + if self.instance_id is not None: + result['InstanceId'] = self.instance_id + if self.message is not None: + result['Message'] = self.message + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.success is not None: + result['Success'] = self.success + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Code') is not None: + self.code = m.get('Code') + if m.get('Events') is not None: + self.events = m.get('Events') + if m.get('HttpStatusCode') is not None: + self.http_status_code = m.get('HttpStatusCode') + if m.get('InstanceId') is not None: + self.instance_id = m.get('InstanceId') + if m.get('Message') is not None: + self.message = m.get('Message') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('Success') is not None: + self.success = m.get('Success') + return self + + +class GetInstanceEventsResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetInstanceEventsResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetInstanceEventsResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetInstanceMetricsRequest(TeaModel): + def __init__( + self, + end_time: str = None, + metric_type: str = None, + start_time: str = None, + time_step: str = None, + ): + self.end_time = end_time + self.metric_type = metric_type + self.start_time = start_time + self.time_step = time_step + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.end_time is not None: + result['EndTime'] = self.end_time + if self.metric_type is not None: + result['MetricType'] = self.metric_type + if self.start_time is not None: + result['StartTime'] = self.start_time + if self.time_step is not None: + result['TimeStep'] = self.time_step + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('EndTime') is not None: + self.end_time = m.get('EndTime') + if m.get('MetricType') is not None: + self.metric_type = m.get('MetricType') + if m.get('StartTime') is not None: + self.start_time = m.get('StartTime') + if m.get('TimeStep') is not None: + self.time_step = m.get('TimeStep') + return self + + +class GetInstanceMetricsResponseBodyPodMetricsMetrics(TeaModel): + def __init__( + self, + time: int = None, + value: float = None, + ): + self.time = time + self.value = value + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.time is not None: + result['Time'] = self.time + if self.value is not None: + result['Value'] = self.value + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Time') is not None: + self.time = m.get('Time') + if m.get('Value') is not None: + self.value = m.get('Value') + return self + + +class GetInstanceMetricsResponseBodyPodMetrics(TeaModel): + def __init__( + self, + metrics: List[GetInstanceMetricsResponseBodyPodMetricsMetrics] = None, + pod_id: str = None, + ): + self.metrics = metrics + self.pod_id = pod_id + + def validate(self): + if self.metrics: + for k in self.metrics: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + result['Metrics'] = [] + if self.metrics is not None: + for k in self.metrics: + result['Metrics'].append(k.to_map() if k else None) + if self.pod_id is not None: + result['PodId'] = self.pod_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + self.metrics = [] + if m.get('Metrics') is not None: + for k in m.get('Metrics'): + temp_model = GetInstanceMetricsResponseBodyPodMetricsMetrics() + self.metrics.append(temp_model.from_map(k)) + if m.get('PodId') is not None: + self.pod_id = m.get('PodId') + return self + + +class GetInstanceMetricsResponseBody(TeaModel): + def __init__( + self, + code: str = None, + http_status_code: int = None, + instance_id: str = None, + message: str = None, + pod_metrics: List[GetInstanceMetricsResponseBodyPodMetrics] = None, + request_id: str = None, + success: bool = None, + ): + self.code = code + self.http_status_code = http_status_code + self.instance_id = instance_id + self.message = message + self.pod_metrics = pod_metrics + self.request_id = request_id + self.success = success + + def validate(self): + if self.pod_metrics: + for k in self.pod_metrics: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.code is not None: + result['Code'] = self.code + if self.http_status_code is not None: + result['HttpStatusCode'] = self.http_status_code + if self.instance_id is not None: + result['InstanceId'] = self.instance_id + if self.message is not None: + result['Message'] = self.message + result['PodMetrics'] = [] + if self.pod_metrics is not None: + for k in self.pod_metrics: + result['PodMetrics'].append(k.to_map() if k else None) + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.success is not None: + result['Success'] = self.success + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Code') is not None: + self.code = m.get('Code') + if m.get('HttpStatusCode') is not None: + self.http_status_code = m.get('HttpStatusCode') + if m.get('InstanceId') is not None: + self.instance_id = m.get('InstanceId') + if m.get('Message') is not None: + self.message = m.get('Message') + self.pod_metrics = [] + if m.get('PodMetrics') is not None: + for k in m.get('PodMetrics'): + temp_model = GetInstanceMetricsResponseBodyPodMetrics() + self.pod_metrics.append(temp_model.from_map(k)) + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('Success') is not None: + self.success = m.get('Success') + return self + + +class GetInstanceMetricsResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetInstanceMetricsResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetInstanceMetricsResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetInstanceShutdownTimerResponseBody(TeaModel): + def __init__( + self, + code: str = None, + due_time: str = None, + gmt_create_time: str = None, + gmt_modified_time: str = None, + http_status_code: int = None, + instance_id: str = None, + message: str = None, + remaining_time_in_ms: int = None, + request_id: str = None, + success: bool = None, + ): + self.code = code + self.due_time = due_time + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time + self.http_status_code = http_status_code + self.instance_id = instance_id + self.message = message + self.remaining_time_in_ms = remaining_time_in_ms + self.request_id = request_id + self.success = success + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.code is not None: + result['Code'] = self.code + if self.due_time is not None: + result['DueTime'] = self.due_time + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + if self.http_status_code is not None: + result['HttpStatusCode'] = self.http_status_code + if self.instance_id is not None: + result['InstanceId'] = self.instance_id + if self.message is not None: + result['Message'] = self.message + if self.remaining_time_in_ms is not None: + result['RemainingTimeInMs'] = self.remaining_time_in_ms + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.success is not None: + result['Success'] = self.success + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Code') is not None: + self.code = m.get('Code') + if m.get('DueTime') is not None: + self.due_time = m.get('DueTime') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + if m.get('HttpStatusCode') is not None: + self.http_status_code = m.get('HttpStatusCode') + if m.get('InstanceId') is not None: + self.instance_id = m.get('InstanceId') + if m.get('Message') is not None: + self.message = m.get('Message') + if m.get('RemainingTimeInMs') is not None: + self.remaining_time_in_ms = m.get('RemainingTimeInMs') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('Success') is not None: + self.success = m.get('Success') + return self + + +class GetInstanceShutdownTimerResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetInstanceShutdownTimerResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetInstanceShutdownTimerResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetInstanceSnapshotResponseBodyLabels(TeaModel): + def __init__( + self, + key: str = None, + value: str = None, + ): + self.key = key + self.value = value + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.key is not None: + result['Key'] = self.key + if self.value is not None: + result['Value'] = self.value + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Key') is not None: + self.key = m.get('Key') + if m.get('Value') is not None: + self.value = m.get('Value') + return self + + +class GetInstanceSnapshotResponseBody(TeaModel): + def __init__( + self, + code: str = None, + exclude_paths: List[str] = None, + gmt_create_time: str = None, + gmt_modified_time: str = None, + http_status_code: int = None, + image_id: str = None, + image_url: str = None, + instance_id: str = None, + labels: List[GetInstanceSnapshotResponseBodyLabels] = None, + message: str = None, + reason_code: str = None, + reason_message: str = None, + request_id: str = None, + snapshot_id: str = None, + snapshot_name: str = None, + status: str = None, + success: bool = None, + ): + self.code = code + self.exclude_paths = exclude_paths + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time + self.http_status_code = http_status_code + self.image_id = image_id + self.image_url = image_url + self.instance_id = instance_id + self.labels = labels + self.message = message + self.reason_code = reason_code + self.reason_message = reason_message + self.request_id = request_id + self.snapshot_id = snapshot_id + self.snapshot_name = snapshot_name + self.status = status + self.success = success + + def validate(self): + if self.labels: + for k in self.labels: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.code is not None: + result['Code'] = self.code + if self.exclude_paths is not None: + result['ExcludePaths'] = self.exclude_paths + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + if self.http_status_code is not None: + result['HttpStatusCode'] = self.http_status_code + if self.image_id is not None: + result['ImageId'] = self.image_id + if self.image_url is not None: + result['ImageUrl'] = self.image_url + if self.instance_id is not None: + result['InstanceId'] = self.instance_id + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + if self.message is not None: + result['Message'] = self.message + if self.reason_code is not None: + result['ReasonCode'] = self.reason_code + if self.reason_message is not None: + result['ReasonMessage'] = self.reason_message + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.snapshot_id is not None: + result['SnapshotId'] = self.snapshot_id + if self.snapshot_name is not None: + result['SnapshotName'] = self.snapshot_name + if self.status is not None: + result['Status'] = self.status + if self.success is not None: + result['Success'] = self.success + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Code') is not None: + self.code = m.get('Code') + if m.get('ExcludePaths') is not None: + self.exclude_paths = m.get('ExcludePaths') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + if m.get('HttpStatusCode') is not None: + self.http_status_code = m.get('HttpStatusCode') + if m.get('ImageId') is not None: + self.image_id = m.get('ImageId') + if m.get('ImageUrl') is not None: + self.image_url = m.get('ImageUrl') + if m.get('InstanceId') is not None: + self.instance_id = m.get('InstanceId') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = GetInstanceSnapshotResponseBodyLabels() + self.labels.append(temp_model.from_map(k)) + if m.get('Message') is not None: + self.message = m.get('Message') + if m.get('ReasonCode') is not None: + self.reason_code = m.get('ReasonCode') + if m.get('ReasonMessage') is not None: + self.reason_message = m.get('ReasonMessage') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('SnapshotId') is not None: + self.snapshot_id = m.get('SnapshotId') + if m.get('SnapshotName') is not None: + self.snapshot_name = m.get('SnapshotName') + if m.get('Status') is not None: + self.status = m.get('Status') + if m.get('Success') is not None: + self.success = m.get('Success') + return self + + +class GetInstanceSnapshotResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetInstanceSnapshotResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetInstanceSnapshotResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetLifecycleRequest(TeaModel): + def __init__( + self, + end_time: str = None, + limit: int = None, + order: str = None, + session_number: int = None, + start_time: str = None, + ): + self.end_time = end_time + self.limit = limit + self.order = order + self.session_number = session_number + self.start_time = start_time + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.end_time is not None: + result['EndTime'] = self.end_time + if self.limit is not None: + result['Limit'] = self.limit + if self.order is not None: + result['Order'] = self.order + if self.session_number is not None: + result['SessionNumber'] = self.session_number + if self.start_time is not None: + result['StartTime'] = self.start_time + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('EndTime') is not None: + self.end_time = m.get('EndTime') + if m.get('Limit') is not None: + self.limit = m.get('Limit') + if m.get('Order') is not None: + self.order = m.get('Order') + if m.get('SessionNumber') is not None: + self.session_number = m.get('SessionNumber') + if m.get('StartTime') is not None: + self.start_time = m.get('StartTime') + return self + + +class GetLifecycleResponseBodyLifecycle(TeaModel): + def __init__( + self, + status: str = None, + reason_code: str = None, + reason_message: str = None, + gmt_create_time: str = None, + ): + self.status = status + self.reason_code = reason_code + self.reason_message = reason_message + self.gmt_create_time = gmt_create_time + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.status is not None: + result['Status'] = self.status + if self.reason_code is not None: + result['ReasonCode'] = self.reason_code + if self.reason_message is not None: + result['ReasonMessage'] = self.reason_message + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Status') is not None: + self.status = m.get('Status') + if m.get('ReasonCode') is not None: + self.reason_code = m.get('ReasonCode') + if m.get('ReasonMessage') is not None: + self.reason_message = m.get('ReasonMessage') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + return self + + +class GetLifecycleResponseBody(TeaModel): + def __init__( + self, + code: str = None, + lifecycle: List[List[GetLifecycleResponseBodyLifecycle]] = None, + message: str = None, + request_id: str = None, + success: bool = None, + total_count: int = None, + ): + self.code = code + self.lifecycle = lifecycle + self.message = message + self.request_id = request_id + self.success = success + self.total_count = total_count + + def validate(self): + if self.lifecycle: + for k in self.lifecycle: + for k1 in k: + if k1: + k1.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.code is not None: + result['Code'] = self.code + result['Lifecycle'] = [] + if self.lifecycle is not None: + for k in self.lifecycle: + l1 = [] + for k1 in k: + l1.append(k1.to_map() if k1 else None) + result['Lifecycle'].append(l1) + if self.message is not None: + result['Message'] = self.message + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.success is not None: + result['Success'] = self.success + if self.total_count is not None: + result['TotalCount'] = self.total_count + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Code') is not None: + self.code = m.get('Code') + self.lifecycle = [] + if m.get('Lifecycle') is not None: + for k in m.get('Lifecycle'): + l1 = [] + for k1 in k: + temp_model = GetLifecycleResponseBodyLifecycle() + l1.append(temp_model.from_map(k1)) + self.lifecycle.append(l1) + if m.get('Message') is not None: + self.message = m.get('Message') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('Success') is not None: + self.success = m.get('Success') + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') + return self + + +class GetLifecycleResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetLifecycleResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetLifecycleResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetResourceGroupStatisticsRequest(TeaModel): + def __init__( + self, + end_time: str = None, + resource_id: str = None, + start_time: str = None, + workspace_ids: str = None, + ): + self.end_time = end_time + self.resource_id = resource_id + self.start_time = start_time + self.workspace_ids = workspace_ids + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.end_time is not None: + result['EndTime'] = self.end_time + if self.resource_id is not None: + result['ResourceId'] = self.resource_id + if self.start_time is not None: + result['StartTime'] = self.start_time + if self.workspace_ids is not None: + result['WorkspaceIds'] = self.workspace_ids + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('EndTime') is not None: + self.end_time = m.get('EndTime') + if m.get('ResourceId') is not None: + self.resource_id = m.get('ResourceId') + if m.get('StartTime') is not None: + self.start_time = m.get('StartTime') + if m.get('WorkspaceIds') is not None: + self.workspace_ids = m.get('WorkspaceIds') + return self + + +class GetResourceGroupStatisticsResponseBody(TeaModel): + def __init__( + self, + code: str = None, + http_status_code: int = None, + message: str = None, + request_id: str = None, + statistics: Dict[str, dict] = None, + success: bool = None, + ): + self.code = code + self.http_status_code = http_status_code + self.message = message + self.request_id = request_id + self.statistics = statistics + self.success = success + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.code is not None: + result['Code'] = self.code + if self.http_status_code is not None: + result['HttpStatusCode'] = self.http_status_code + if self.message is not None: + result['Message'] = self.message + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.statistics is not None: + result['Statistics'] = self.statistics + if self.success is not None: + result['Success'] = self.success + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Code') is not None: + self.code = m.get('Code') + if m.get('HttpStatusCode') is not None: + self.http_status_code = m.get('HttpStatusCode') + if m.get('Message') is not None: + self.message = m.get('Message') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('Statistics') is not None: + self.statistics = m.get('Statistics') + if m.get('Success') is not None: + self.success = m.get('Success') + return self + + +class GetResourceGroupStatisticsResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetResourceGroupStatisticsResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetResourceGroupStatisticsResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetTokenRequest(TeaModel): + def __init__( + self, + expire_time: int = None, + instance_id: str = None, + ): + self.expire_time = expire_time + self.instance_id = instance_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.expire_time is not None: + result['ExpireTime'] = self.expire_time + if self.instance_id is not None: + result['InstanceId'] = self.instance_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('ExpireTime') is not None: + self.expire_time = m.get('ExpireTime') + if m.get('InstanceId') is not None: + self.instance_id = m.get('InstanceId') + return self + + +class GetTokenResponseBody(TeaModel): + def __init__( + self, + code: str = None, + message: str = None, + request_id: str = None, + success: bool = None, + token: str = None, + ): + self.code = code + self.message = message + self.request_id = request_id + self.success = success + self.token = token + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.code is not None: + result['Code'] = self.code + if self.message is not None: + result['Message'] = self.message + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.success is not None: + result['Success'] = self.success + if self.token is not None: + result['Token'] = self.token + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Code') is not None: + self.code = m.get('Code') + if m.get('Message') is not None: + self.message = m.get('Message') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('Success') is not None: + self.success = m.get('Success') + if m.get('Token') is not None: + self.token = m.get('Token') + return self + + +class GetTokenResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetTokenResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetTokenResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetUserConfigResponseBodyFreeTier(TeaModel): + def __init__( + self, + end_time: str = None, + init_base_unit: str = None, + init_base_value: float = None, + init_show_unit: str = None, + init_show_value: str = None, + is_free_tier_user: bool = None, + period_base_unit: str = None, + period_base_value: float = None, + period_show_unit: str = None, + period_show_value: str = None, + start_time: str = None, + status: str = None, + ): + self.end_time = end_time + self.init_base_unit = init_base_unit + self.init_base_value = init_base_value + self.init_show_unit = init_show_unit + self.init_show_value = init_show_value + self.is_free_tier_user = is_free_tier_user + self.period_base_unit = period_base_unit + self.period_base_value = period_base_value + self.period_show_unit = period_show_unit + self.period_show_value = period_show_value + self.start_time = start_time + self.status = status + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.end_time is not None: + result['EndTime'] = self.end_time + if self.init_base_unit is not None: + result['InitBaseUnit'] = self.init_base_unit + if self.init_base_value is not None: + result['InitBaseValue'] = self.init_base_value + if self.init_show_unit is not None: + result['InitShowUnit'] = self.init_show_unit + if self.init_show_value is not None: + result['InitShowValue'] = self.init_show_value + if self.is_free_tier_user is not None: + result['IsFreeTierUser'] = self.is_free_tier_user + if self.period_base_unit is not None: + result['PeriodBaseUnit'] = self.period_base_unit + if self.period_base_value is not None: + result['PeriodBaseValue'] = self.period_base_value + if self.period_show_unit is not None: + result['PeriodShowUnit'] = self.period_show_unit + if self.period_show_value is not None: + result['PeriodShowValue'] = self.period_show_value + if self.start_time is not None: + result['StartTime'] = self.start_time + if self.status is not None: + result['Status'] = self.status + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('EndTime') is not None: + self.end_time = m.get('EndTime') + if m.get('InitBaseUnit') is not None: + self.init_base_unit = m.get('InitBaseUnit') + if m.get('InitBaseValue') is not None: + self.init_base_value = m.get('InitBaseValue') + if m.get('InitShowUnit') is not None: + self.init_show_unit = m.get('InitShowUnit') + if m.get('InitShowValue') is not None: + self.init_show_value = m.get('InitShowValue') + if m.get('IsFreeTierUser') is not None: + self.is_free_tier_user = m.get('IsFreeTierUser') + if m.get('PeriodBaseUnit') is not None: + self.period_base_unit = m.get('PeriodBaseUnit') + if m.get('PeriodBaseValue') is not None: + self.period_base_value = m.get('PeriodBaseValue') + if m.get('PeriodShowUnit') is not None: + self.period_show_unit = m.get('PeriodShowUnit') + if m.get('PeriodShowValue') is not None: + self.period_show_value = m.get('PeriodShowValue') + if m.get('StartTime') is not None: + self.start_time = m.get('StartTime') + if m.get('Status') is not None: + self.status = m.get('Status') + return self + + +class GetUserConfigResponseBody(TeaModel): + def __init__( + self, + account_sufficient: bool = None, + code: str = None, + enable_eci_disk: bool = None, + free_tier: GetUserConfigResponseBodyFreeTier = None, + free_tier_spec_available: bool = None, + http_status_code: int = None, + message: str = None, + request_id: str = None, + success: bool = None, + ): + self.account_sufficient = account_sufficient + self.code = code + self.enable_eci_disk = enable_eci_disk + self.free_tier = free_tier + self.free_tier_spec_available = free_tier_spec_available + self.http_status_code = http_status_code + self.message = message + self.request_id = request_id + self.success = success + + def validate(self): + if self.free_tier: + self.free_tier.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.account_sufficient is not None: + result['AccountSufficient'] = self.account_sufficient + if self.code is not None: + result['Code'] = self.code + if self.enable_eci_disk is not None: + result['EnableEciDisk'] = self.enable_eci_disk + if self.free_tier is not None: + result['FreeTier'] = self.free_tier.to_map() + if self.free_tier_spec_available is not None: + result['FreeTierSpecAvailable'] = self.free_tier_spec_available + if self.http_status_code is not None: + result['HttpStatusCode'] = self.http_status_code + if self.message is not None: + result['Message'] = self.message + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.success is not None: + result['Success'] = self.success + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('AccountSufficient') is not None: + self.account_sufficient = m.get('AccountSufficient') + if m.get('Code') is not None: + self.code = m.get('Code') + if m.get('EnableEciDisk') is not None: + self.enable_eci_disk = m.get('EnableEciDisk') + if m.get('FreeTier') is not None: + temp_model = GetUserConfigResponseBodyFreeTier() + self.free_tier = temp_model.from_map(m['FreeTier']) + if m.get('FreeTierSpecAvailable') is not None: + self.free_tier_spec_available = m.get('FreeTierSpecAvailable') + if m.get('HttpStatusCode') is not None: + self.http_status_code = m.get('HttpStatusCode') + if m.get('Message') is not None: + self.message = m.get('Message') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('Success') is not None: + self.success = m.get('Success') + return self + + +class GetUserConfigResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetUserConfigResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetUserConfigResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class ListEcsSpecsRequest(TeaModel): + def __init__( + self, + accelerator_type: str = None, + order: str = None, + page_number: int = None, + page_size: int = None, + sort_by: str = None, + ): + self.accelerator_type = accelerator_type + self.order = order + self.page_number = page_number + self.page_size = page_size + self.sort_by = sort_by + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.accelerator_type is not None: + result['AcceleratorType'] = self.accelerator_type + if self.order is not None: + result['Order'] = self.order + if self.page_number is not None: + result['PageNumber'] = self.page_number + if self.page_size is not None: + result['PageSize'] = self.page_size + if self.sort_by is not None: + result['SortBy'] = self.sort_by + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('AcceleratorType') is not None: + self.accelerator_type = m.get('AcceleratorType') + if m.get('Order') is not None: + self.order = m.get('Order') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + if m.get('SortBy') is not None: + self.sort_by = m.get('SortBy') + return self + + +class ListEcsSpecsResponseBodyEcsSpecsLabels(TeaModel): + def __init__( + self, + key: str = None, + value: str = None, + ): + self.key = key + self.value = value + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.key is not None: + result['Key'] = self.key + if self.value is not None: + result['Value'] = self.value + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Key') is not None: + self.key = m.get('Key') + if m.get('Value') is not None: + self.value = m.get('Value') + return self + + +class ListEcsSpecsResponseBodyEcsSpecs(TeaModel): + def __init__( + self, + accelerator_type: str = None, + cpu: int = None, + currency: str = None, + gpu: int = None, + gputype: str = None, + instance_bandwidth_rx: int = None, + instance_type: str = None, + is_available: bool = None, + labels: List[ListEcsSpecsResponseBodyEcsSpecsLabels] = None, + memory: float = None, + price: float = None, + system_disk_capacity: int = None, + ): + self.accelerator_type = accelerator_type + self.cpu = cpu + self.currency = currency + self.gpu = gpu + self.gputype = gputype + self.instance_bandwidth_rx = instance_bandwidth_rx + self.instance_type = instance_type + self.is_available = is_available + self.labels = labels + self.memory = memory + self.price = price + self.system_disk_capacity = system_disk_capacity + + def validate(self): + if self.labels: + for k in self.labels: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.accelerator_type is not None: + result['AcceleratorType'] = self.accelerator_type + if self.cpu is not None: + result['CPU'] = self.cpu + if self.currency is not None: + result['Currency'] = self.currency + if self.gpu is not None: + result['GPU'] = self.gpu + if self.gputype is not None: + result['GPUType'] = self.gputype + if self.instance_bandwidth_rx is not None: + result['InstanceBandwidthRx'] = self.instance_bandwidth_rx + if self.instance_type is not None: + result['InstanceType'] = self.instance_type + if self.is_available is not None: + result['IsAvailable'] = self.is_available + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + if self.memory is not None: + result['Memory'] = self.memory + if self.price is not None: + result['Price'] = self.price + if self.system_disk_capacity is not None: + result['SystemDiskCapacity'] = self.system_disk_capacity + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('AcceleratorType') is not None: + self.accelerator_type = m.get('AcceleratorType') + if m.get('CPU') is not None: + self.cpu = m.get('CPU') + if m.get('Currency') is not None: + self.currency = m.get('Currency') + if m.get('GPU') is not None: + self.gpu = m.get('GPU') + if m.get('GPUType') is not None: + self.gputype = m.get('GPUType') + if m.get('InstanceBandwidthRx') is not None: + self.instance_bandwidth_rx = m.get('InstanceBandwidthRx') + if m.get('InstanceType') is not None: + self.instance_type = m.get('InstanceType') + if m.get('IsAvailable') is not None: + self.is_available = m.get('IsAvailable') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = ListEcsSpecsResponseBodyEcsSpecsLabels() + self.labels.append(temp_model.from_map(k)) + if m.get('Memory') is not None: + self.memory = m.get('Memory') + if m.get('Price') is not None: + self.price = m.get('Price') + if m.get('SystemDiskCapacity') is not None: + self.system_disk_capacity = m.get('SystemDiskCapacity') + return self + + +class ListEcsSpecsResponseBody(TeaModel): + def __init__( + self, + code: str = None, + ecs_specs: List[ListEcsSpecsResponseBodyEcsSpecs] = None, + http_status_code: int = None, + message: str = None, + request_id: str = None, + success: bool = None, + total_count: int = None, + ): + self.code = code + self.ecs_specs = ecs_specs + self.http_status_code = http_status_code + self.message = message + self.request_id = request_id + self.success = success + self.total_count = total_count + + def validate(self): + if self.ecs_specs: + for k in self.ecs_specs: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.code is not None: + result['Code'] = self.code + result['EcsSpecs'] = [] + if self.ecs_specs is not None: + for k in self.ecs_specs: + result['EcsSpecs'].append(k.to_map() if k else None) + if self.http_status_code is not None: + result['HttpStatusCode'] = self.http_status_code + if self.message is not None: + result['Message'] = self.message + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.success is not None: + result['Success'] = self.success + if self.total_count is not None: + result['TotalCount'] = self.total_count + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Code') is not None: + self.code = m.get('Code') + self.ecs_specs = [] + if m.get('EcsSpecs') is not None: + for k in m.get('EcsSpecs'): + temp_model = ListEcsSpecsResponseBodyEcsSpecs() + self.ecs_specs.append(temp_model.from_map(k)) + if m.get('HttpStatusCode') is not None: + self.http_status_code = m.get('HttpStatusCode') + if m.get('Message') is not None: + self.message = m.get('Message') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('Success') is not None: + self.success = m.get('Success') + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') + return self + + +class ListEcsSpecsResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: ListEcsSpecsResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = ListEcsSpecsResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class ListInstanceSnapshotRequest(TeaModel): + def __init__( + self, + order: str = None, + page_number: int = None, + page_size: int = None, + sort_by: str = None, + ): + self.order = order + self.page_number = page_number + self.page_size = page_size + self.sort_by = sort_by + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.order is not None: + result['Order'] = self.order + if self.page_number is not None: + result['PageNumber'] = self.page_number + if self.page_size is not None: + result['PageSize'] = self.page_size + if self.sort_by is not None: + result['SortBy'] = self.sort_by + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Order') is not None: + self.order = m.get('Order') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + if m.get('SortBy') is not None: + self.sort_by = m.get('SortBy') + return self + + +class ListInstanceSnapshotResponseBodySnapshotsLabels(TeaModel): + def __init__( + self, + key: str = None, + value: str = None, + ): + self.key = key + self.value = value + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.key is not None: + result['Key'] = self.key + if self.value is not None: + result['Value'] = self.value + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Key') is not None: + self.key = m.get('Key') + if m.get('Value') is not None: + self.value = m.get('Value') + return self + + +class ListInstanceSnapshotResponseBodySnapshots(TeaModel): + def __init__( + self, + exclude_paths: List[str] = None, + gmt_create_time: str = None, + gmt_modified_time: str = None, + image_id: str = None, + image_url: str = None, + instance_id: str = None, + labels: List[ListInstanceSnapshotResponseBodySnapshotsLabels] = None, + reason_code: str = None, + reason_message: str = None, + snapshot_id: str = None, + snapshot_name: str = None, + status: str = None, + ): + self.exclude_paths = exclude_paths + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time + self.image_id = image_id + self.image_url = image_url + self.instance_id = instance_id + self.labels = labels + self.reason_code = reason_code + self.reason_message = reason_message + self.snapshot_id = snapshot_id + self.snapshot_name = snapshot_name + self.status = status + + def validate(self): + if self.labels: + for k in self.labels: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.exclude_paths is not None: + result['ExcludePaths'] = self.exclude_paths + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + if self.image_id is not None: + result['ImageId'] = self.image_id + if self.image_url is not None: + result['ImageUrl'] = self.image_url + if self.instance_id is not None: + result['InstanceId'] = self.instance_id + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + if self.reason_code is not None: + result['ReasonCode'] = self.reason_code + if self.reason_message is not None: + result['ReasonMessage'] = self.reason_message + if self.snapshot_id is not None: + result['SnapshotId'] = self.snapshot_id + if self.snapshot_name is not None: + result['SnapshotName'] = self.snapshot_name + if self.status is not None: + result['Status'] = self.status + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('ExcludePaths') is not None: + self.exclude_paths = m.get('ExcludePaths') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + if m.get('ImageId') is not None: + self.image_id = m.get('ImageId') + if m.get('ImageUrl') is not None: + self.image_url = m.get('ImageUrl') + if m.get('InstanceId') is not None: + self.instance_id = m.get('InstanceId') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = ListInstanceSnapshotResponseBodySnapshotsLabels() + self.labels.append(temp_model.from_map(k)) + if m.get('ReasonCode') is not None: + self.reason_code = m.get('ReasonCode') + if m.get('ReasonMessage') is not None: + self.reason_message = m.get('ReasonMessage') + if m.get('SnapshotId') is not None: + self.snapshot_id = m.get('SnapshotId') + if m.get('SnapshotName') is not None: + self.snapshot_name = m.get('SnapshotName') + if m.get('Status') is not None: + self.status = m.get('Status') + return self + + +class ListInstanceSnapshotResponseBody(TeaModel): + def __init__( + self, + code: str = None, + http_status_code: int = None, + message: str = None, + request_id: str = None, + snapshots: List[ListInstanceSnapshotResponseBodySnapshots] = None, + success: bool = None, + total_count: int = None, + ): + self.code = code + self.http_status_code = http_status_code + self.message = message + self.request_id = request_id + self.snapshots = snapshots + self.success = success + self.total_count = total_count + + def validate(self): + if self.snapshots: + for k in self.snapshots: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.code is not None: + result['Code'] = self.code + if self.http_status_code is not None: + result['HttpStatusCode'] = self.http_status_code + if self.message is not None: + result['Message'] = self.message + if self.request_id is not None: + result['RequestId'] = self.request_id + result['Snapshots'] = [] + if self.snapshots is not None: + for k in self.snapshots: + result['Snapshots'].append(k.to_map() if k else None) + if self.success is not None: + result['Success'] = self.success + if self.total_count is not None: + result['TotalCount'] = self.total_count + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Code') is not None: + self.code = m.get('Code') + if m.get('HttpStatusCode') is not None: + self.http_status_code = m.get('HttpStatusCode') + if m.get('Message') is not None: + self.message = m.get('Message') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + self.snapshots = [] + if m.get('Snapshots') is not None: + for k in m.get('Snapshots'): + temp_model = ListInstanceSnapshotResponseBodySnapshots() + self.snapshots.append(temp_model.from_map(k)) + if m.get('Success') is not None: + self.success = m.get('Success') + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') + return self + + +class ListInstanceSnapshotResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: ListInstanceSnapshotResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = ListInstanceSnapshotResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class ListInstanceStatisticsRequest(TeaModel): + def __init__( + self, + workspace_ids: str = None, + ): + self.workspace_ids = workspace_ids + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.workspace_ids is not None: + result['WorkspaceIds'] = self.workspace_ids + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('WorkspaceIds') is not None: + self.workspace_ids = m.get('WorkspaceIds') + return self + + +class ListInstanceStatisticsResponseBody(TeaModel): + def __init__( + self, + code: str = None, + http_status_code: int = None, + message: str = None, + request_id: str = None, + statistics: Dict[str, dict] = None, + success: bool = None, + ): + self.code = code + self.http_status_code = http_status_code + self.message = message + self.request_id = request_id + self.statistics = statistics + self.success = success + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.code is not None: + result['Code'] = self.code + if self.http_status_code is not None: + result['HttpStatusCode'] = self.http_status_code + if self.message is not None: + result['Message'] = self.message + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.statistics is not None: + result['Statistics'] = self.statistics + if self.success is not None: + result['Success'] = self.success + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Code') is not None: + self.code = m.get('Code') + if m.get('HttpStatusCode') is not None: + self.http_status_code = m.get('HttpStatusCode') + if m.get('Message') is not None: + self.message = m.get('Message') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('Statistics') is not None: + self.statistics = m.get('Statistics') + if m.get('Success') is not None: + self.success = m.get('Success') + return self + + +class ListInstanceStatisticsResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: ListInstanceStatisticsResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = ListInstanceStatisticsResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class ListInstancesRequest(TeaModel): + def __init__( + self, + accelerator_type: str = None, + accessibility: str = None, + instance_id: str = None, + instance_name: str = None, + order: str = None, + page_number: int = None, + page_size: int = None, + payment_type: str = None, + resource_id: str = None, + sort_by: str = None, + status: str = None, + workspace_id: str = None, + ): + self.accelerator_type = accelerator_type + self.accessibility = accessibility + self.instance_id = instance_id + self.instance_name = instance_name + self.order = order + self.page_number = page_number + self.page_size = page_size + self.payment_type = payment_type + self.resource_id = resource_id + self.sort_by = sort_by + self.status = status + self.workspace_id = workspace_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.accelerator_type is not None: + result['AcceleratorType'] = self.accelerator_type + if self.accessibility is not None: + result['Accessibility'] = self.accessibility + if self.instance_id is not None: + result['InstanceId'] = self.instance_id + if self.instance_name is not None: + result['InstanceName'] = self.instance_name + if self.order is not None: + result['Order'] = self.order + if self.page_number is not None: + result['PageNumber'] = self.page_number + if self.page_size is not None: + result['PageSize'] = self.page_size + if self.payment_type is not None: + result['PaymentType'] = self.payment_type + if self.resource_id is not None: + result['ResourceId'] = self.resource_id + if self.sort_by is not None: + result['SortBy'] = self.sort_by + if self.status is not None: + result['Status'] = self.status + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('AcceleratorType') is not None: + self.accelerator_type = m.get('AcceleratorType') + if m.get('Accessibility') is not None: + self.accessibility = m.get('Accessibility') + if m.get('InstanceId') is not None: + self.instance_id = m.get('InstanceId') + if m.get('InstanceName') is not None: + self.instance_name = m.get('InstanceName') + if m.get('Order') is not None: + self.order = m.get('Order') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + if m.get('PaymentType') is not None: + self.payment_type = m.get('PaymentType') + if m.get('ResourceId') is not None: + self.resource_id = m.get('ResourceId') + if m.get('SortBy') is not None: + self.sort_by = m.get('SortBy') + if m.get('Status') is not None: + self.status = m.get('Status') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') + return self + + +class ListInstancesResponseBodyInstancesCloudDisks(TeaModel): + def __init__( + self, + capacity: str = None, + mount_path: str = None, + path: str = None, + sub_type: str = None, + ): + self.capacity = capacity + self.mount_path = mount_path + self.path = path + self.sub_type = sub_type + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.capacity is not None: + result['Capacity'] = self.capacity + if self.mount_path is not None: + result['MountPath'] = self.mount_path + if self.path is not None: + result['Path'] = self.path + if self.sub_type is not None: + result['SubType'] = self.sub_type + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Capacity') is not None: + self.capacity = m.get('Capacity') + if m.get('MountPath') is not None: + self.mount_path = m.get('MountPath') + if m.get('Path') is not None: + self.path = m.get('Path') + if m.get('SubType') is not None: + self.sub_type = m.get('SubType') + return self + + +class ListInstancesResponseBodyInstancesDatasets(TeaModel): + def __init__( + self, + dataset_id: str = None, + mount_path: str = None, + ): + self.dataset_id = dataset_id + self.mount_path = mount_path + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.dataset_id is not None: + result['DatasetId'] = self.dataset_id + if self.mount_path is not None: + result['MountPath'] = self.mount_path + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('DatasetId') is not None: + self.dataset_id = m.get('DatasetId') + if m.get('MountPath') is not None: + self.mount_path = m.get('MountPath') + return self + + +class ListInstancesResponseBodyInstancesIdleInstanceCuller(TeaModel): + def __init__( + self, + cpu_percent_threshold: int = None, + gpu_percent_threshold: int = None, + idle_time_in_minutes: int = None, + instance_id: str = None, + max_idle_time_in_minutes: int = None, + ): + self.cpu_percent_threshold = cpu_percent_threshold + self.gpu_percent_threshold = gpu_percent_threshold + self.idle_time_in_minutes = idle_time_in_minutes + self.instance_id = instance_id + self.max_idle_time_in_minutes = max_idle_time_in_minutes + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.cpu_percent_threshold is not None: + result['CpuPercentThreshold'] = self.cpu_percent_threshold + if self.gpu_percent_threshold is not None: + result['GpuPercentThreshold'] = self.gpu_percent_threshold + if self.idle_time_in_minutes is not None: + result['IdleTimeInMinutes'] = self.idle_time_in_minutes + if self.instance_id is not None: + result['InstanceId'] = self.instance_id + if self.max_idle_time_in_minutes is not None: + result['MaxIdleTimeInMinutes'] = self.max_idle_time_in_minutes + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('CpuPercentThreshold') is not None: + self.cpu_percent_threshold = m.get('CpuPercentThreshold') + if m.get('GpuPercentThreshold') is not None: + self.gpu_percent_threshold = m.get('GpuPercentThreshold') + if m.get('IdleTimeInMinutes') is not None: + self.idle_time_in_minutes = m.get('IdleTimeInMinutes') + if m.get('InstanceId') is not None: + self.instance_id = m.get('InstanceId') + if m.get('MaxIdleTimeInMinutes') is not None: + self.max_idle_time_in_minutes = m.get('MaxIdleTimeInMinutes') + return self + + +class ListInstancesResponseBodyInstancesInstanceShutdownTimer(TeaModel): + def __init__( + self, + due_time: str = None, + gmt_create_time: str = None, + gmt_modified_time: str = None, + instance_id: str = None, + remaining_time_in_ms: int = None, + ): + self.due_time = due_time + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time + self.instance_id = instance_id + self.remaining_time_in_ms = remaining_time_in_ms + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.due_time is not None: + result['DueTime'] = self.due_time + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + if self.instance_id is not None: + result['InstanceId'] = self.instance_id + if self.remaining_time_in_ms is not None: + result['RemainingTimeInMs'] = self.remaining_time_in_ms + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('DueTime') is not None: + self.due_time = m.get('DueTime') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + if m.get('InstanceId') is not None: + self.instance_id = m.get('InstanceId') + if m.get('RemainingTimeInMs') is not None: + self.remaining_time_in_ms = m.get('RemainingTimeInMs') + return self + + +class ListInstancesResponseBodyInstancesInstanceSnapshotList(TeaModel): + def __init__( + self, + gmt_create_time: str = None, + gmt_modified_time: str = None, + image_id: str = None, + image_name: str = None, + image_url: str = None, + reason_code: str = None, + reason_message: str = None, + repository_url: str = None, + status: str = None, + ): + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time + self.image_id = image_id + self.image_name = image_name + self.image_url = image_url + self.reason_code = reason_code + self.reason_message = reason_message + self.repository_url = repository_url + self.status = status + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + if self.image_id is not None: + result['ImageId'] = self.image_id + if self.image_name is not None: + result['ImageName'] = self.image_name + if self.image_url is not None: + result['ImageUrl'] = self.image_url + if self.reason_code is not None: + result['ReasonCode'] = self.reason_code + if self.reason_message is not None: + result['ReasonMessage'] = self.reason_message + if self.repository_url is not None: + result['RepositoryUrl'] = self.repository_url + if self.status is not None: + result['Status'] = self.status + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + if m.get('ImageId') is not None: + self.image_id = m.get('ImageId') + if m.get('ImageName') is not None: + self.image_name = m.get('ImageName') + if m.get('ImageUrl') is not None: + self.image_url = m.get('ImageUrl') + if m.get('ReasonCode') is not None: + self.reason_code = m.get('ReasonCode') + if m.get('ReasonMessage') is not None: + self.reason_message = m.get('ReasonMessage') + if m.get('RepositoryUrl') is not None: + self.repository_url = m.get('RepositoryUrl') + if m.get('Status') is not None: + self.status = m.get('Status') + return self + + +class ListInstancesResponseBodyInstancesLabels(TeaModel): + def __init__( + self, + key: str = None, + value: str = None, + ): + self.key = key + self.value = value + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.key is not None: + result['Key'] = self.key + if self.value is not None: + result['Value'] = self.value + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Key') is not None: + self.key = m.get('Key') + if m.get('Value') is not None: + self.value = m.get('Value') + return self + + +class ListInstancesResponseBodyInstancesLatestSnapshot(TeaModel): + def __init__( + self, + gmt_create_time: str = None, + gmt_modified_time: str = None, + image_id: str = None, + image_name: str = None, + image_url: str = None, + reason_code: str = None, + reason_message: str = None, + repository_url: str = None, + status: str = None, + ): + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time + self.image_id = image_id + self.image_name = image_name + self.image_url = image_url + self.reason_code = reason_code + self.reason_message = reason_message + self.repository_url = repository_url + self.status = status + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + if self.image_id is not None: + result['ImageId'] = self.image_id + if self.image_name is not None: + result['ImageName'] = self.image_name + if self.image_url is not None: + result['ImageUrl'] = self.image_url + if self.reason_code is not None: + result['ReasonCode'] = self.reason_code + if self.reason_message is not None: + result['ReasonMessage'] = self.reason_message + if self.repository_url is not None: + result['RepositoryUrl'] = self.repository_url + if self.status is not None: + result['Status'] = self.status + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + if m.get('ImageId') is not None: + self.image_id = m.get('ImageId') + if m.get('ImageName') is not None: + self.image_name = m.get('ImageName') + if m.get('ImageUrl') is not None: + self.image_url = m.get('ImageUrl') + if m.get('ReasonCode') is not None: + self.reason_code = m.get('ReasonCode') + if m.get('ReasonMessage') is not None: + self.reason_message = m.get('ReasonMessage') + if m.get('RepositoryUrl') is not None: + self.repository_url = m.get('RepositoryUrl') + if m.get('Status') is not None: + self.status = m.get('Status') + return self + + +class ListInstancesResponseBodyInstancesRequestedResource(TeaModel): + def __init__( + self, + cpu: str = None, + gpu: str = None, + gputype: str = None, + memory: str = None, + shared_memory: str = None, + ): + self.cpu = cpu + self.gpu = gpu + self.gputype = gputype + self.memory = memory + self.shared_memory = shared_memory + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.cpu is not None: + result['CPU'] = self.cpu + if self.gpu is not None: + result['GPU'] = self.gpu + if self.gputype is not None: + result['GPUType'] = self.gputype + if self.memory is not None: + result['Memory'] = self.memory + if self.shared_memory is not None: + result['SharedMemory'] = self.shared_memory + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('CPU') is not None: + self.cpu = m.get('CPU') + if m.get('GPU') is not None: + self.gpu = m.get('GPU') + if m.get('GPUType') is not None: + self.gputype = m.get('GPUType') + if m.get('Memory') is not None: + self.memory = m.get('Memory') + if m.get('SharedMemory') is not None: + self.shared_memory = m.get('SharedMemory') + return self + + +class ListInstancesResponseBodyInstancesUserVpc(TeaModel): + def __init__( + self, + default_route: str = None, + extended_cidrs: List[str] = None, + forward_infos: List[ForwardInfoResponse] = None, + security_group_id: str = None, + v_switch_id: str = None, + vpc_id: str = None, + ): + self.default_route = default_route + self.extended_cidrs = extended_cidrs + self.forward_infos = forward_infos + self.security_group_id = security_group_id + self.v_switch_id = v_switch_id + self.vpc_id = vpc_id + + def validate(self): + if self.forward_infos: + for k in self.forward_infos: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.default_route is not None: + result['DefaultRoute'] = self.default_route + if self.extended_cidrs is not None: + result['ExtendedCIDRs'] = self.extended_cidrs + result['ForwardInfos'] = [] + if self.forward_infos is not None: + for k in self.forward_infos: + result['ForwardInfos'].append(k.to_map() if k else None) + if self.security_group_id is not None: + result['SecurityGroupId'] = self.security_group_id + if self.v_switch_id is not None: + result['VSwitchId'] = self.v_switch_id + if self.vpc_id is not None: + result['VpcId'] = self.vpc_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('DefaultRoute') is not None: + self.default_route = m.get('DefaultRoute') + if m.get('ExtendedCIDRs') is not None: + self.extended_cidrs = m.get('ExtendedCIDRs') + self.forward_infos = [] + if m.get('ForwardInfos') is not None: + for k in m.get('ForwardInfos'): + temp_model = ForwardInfoResponse() + self.forward_infos.append(temp_model.from_map(k)) + if m.get('SecurityGroupId') is not None: + self.security_group_id = m.get('SecurityGroupId') + if m.get('VSwitchId') is not None: + self.v_switch_id = m.get('VSwitchId') + if m.get('VpcId') is not None: + self.vpc_id = m.get('VpcId') + return self + + +class ListInstancesResponseBodyInstances(TeaModel): + def __init__( + self, + accelerator_type: str = None, + accessibility: str = None, + accumulated_running_time_in_ms: int = None, + cloud_disks: List[ListInstancesResponseBodyInstancesCloudDisks] = None, + datasets: List[ListInstancesResponseBodyInstancesDatasets] = None, + driver: str = None, + ecs_spec: str = None, + environment_variables: Dict[str, str] = None, + gmt_create_time: str = None, + gmt_modified_time: str = None, + idle_instance_culler: ListInstancesResponseBodyInstancesIdleInstanceCuller = None, + image_id: str = None, + image_name: str = None, + image_url: str = None, + instance_id: str = None, + instance_name: str = None, + instance_shutdown_timer: ListInstancesResponseBodyInstancesInstanceShutdownTimer = None, + instance_snapshot_list: List[ListInstancesResponseBodyInstancesInstanceSnapshotList] = None, + instance_url: str = None, + jupyterlab_url: str = None, + labels: List[ListInstancesResponseBodyInstancesLabels] = None, + latest_snapshot: ListInstancesResponseBodyInstancesLatestSnapshot = None, + payment_type: str = None, + priority: int = None, + reason_code: str = None, + reason_message: str = None, + requested_resource: ListInstancesResponseBodyInstancesRequestedResource = None, + resource_id: str = None, + resource_name: str = None, + status: str = None, + terminal_url: str = None, + user_id: str = None, + user_name: str = None, + user_vpc: ListInstancesResponseBodyInstancesUserVpc = None, + web_ideurl: str = None, + workspace_id: str = None, + workspace_name: str = None, + workspace_source: str = None, + ): + self.accelerator_type = accelerator_type + self.accessibility = accessibility + self.accumulated_running_time_in_ms = accumulated_running_time_in_ms + self.cloud_disks = cloud_disks + self.datasets = datasets + self.driver = driver + self.ecs_spec = ecs_spec + self.environment_variables = environment_variables + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time + self.idle_instance_culler = idle_instance_culler + self.image_id = image_id + self.image_name = image_name + self.image_url = image_url + self.instance_id = instance_id + self.instance_name = instance_name + self.instance_shutdown_timer = instance_shutdown_timer + self.instance_snapshot_list = instance_snapshot_list + self.instance_url = instance_url + # Jupyterlab Url。 + self.jupyterlab_url = jupyterlab_url + self.labels = labels + self.latest_snapshot = latest_snapshot + self.payment_type = payment_type + self.priority = priority + self.reason_code = reason_code + self.reason_message = reason_message + self.requested_resource = requested_resource + self.resource_id = resource_id + self.resource_name = resource_name + self.status = status + self.terminal_url = terminal_url + self.user_id = user_id + self.user_name = user_name + self.user_vpc = user_vpc + # Web IDE url。 + self.web_ideurl = web_ideurl + self.workspace_id = workspace_id + self.workspace_name = workspace_name + self.workspace_source = workspace_source + + def validate(self): + if self.cloud_disks: + for k in self.cloud_disks: + if k: + k.validate() + if self.datasets: + for k in self.datasets: + if k: + k.validate() + if self.idle_instance_culler: + self.idle_instance_culler.validate() + if self.instance_shutdown_timer: + self.instance_shutdown_timer.validate() + if self.instance_snapshot_list: + for k in self.instance_snapshot_list: + if k: + k.validate() + if self.labels: + for k in self.labels: + if k: + k.validate() + if self.latest_snapshot: + self.latest_snapshot.validate() + if self.requested_resource: + self.requested_resource.validate() + if self.user_vpc: + self.user_vpc.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.accelerator_type is not None: + result['AcceleratorType'] = self.accelerator_type + if self.accessibility is not None: + result['Accessibility'] = self.accessibility + if self.accumulated_running_time_in_ms is not None: + result['AccumulatedRunningTimeInMs'] = self.accumulated_running_time_in_ms + result['CloudDisks'] = [] + if self.cloud_disks is not None: + for k in self.cloud_disks: + result['CloudDisks'].append(k.to_map() if k else None) + result['Datasets'] = [] + if self.datasets is not None: + for k in self.datasets: + result['Datasets'].append(k.to_map() if k else None) + if self.driver is not None: + result['Driver'] = self.driver + if self.ecs_spec is not None: + result['EcsSpec'] = self.ecs_spec + if self.environment_variables is not None: + result['EnvironmentVariables'] = self.environment_variables + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + if self.idle_instance_culler is not None: + result['IdleInstanceCuller'] = self.idle_instance_culler.to_map() + if self.image_id is not None: + result['ImageId'] = self.image_id + if self.image_name is not None: + result['ImageName'] = self.image_name + if self.image_url is not None: + result['ImageUrl'] = self.image_url + if self.instance_id is not None: + result['InstanceId'] = self.instance_id + if self.instance_name is not None: + result['InstanceName'] = self.instance_name + if self.instance_shutdown_timer is not None: + result['InstanceShutdownTimer'] = self.instance_shutdown_timer.to_map() + result['InstanceSnapshotList'] = [] + if self.instance_snapshot_list is not None: + for k in self.instance_snapshot_list: + result['InstanceSnapshotList'].append(k.to_map() if k else None) + if self.instance_url is not None: + result['InstanceUrl'] = self.instance_url + if self.jupyterlab_url is not None: + result['JupyterlabUrl'] = self.jupyterlab_url + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + if self.latest_snapshot is not None: + result['LatestSnapshot'] = self.latest_snapshot.to_map() + if self.payment_type is not None: + result['PaymentType'] = self.payment_type + if self.priority is not None: + result['Priority'] = self.priority + if self.reason_code is not None: + result['ReasonCode'] = self.reason_code + if self.reason_message is not None: + result['ReasonMessage'] = self.reason_message + if self.requested_resource is not None: + result['RequestedResource'] = self.requested_resource.to_map() + if self.resource_id is not None: + result['ResourceId'] = self.resource_id + if self.resource_name is not None: + result['ResourceName'] = self.resource_name + if self.status is not None: + result['Status'] = self.status + if self.terminal_url is not None: + result['TerminalUrl'] = self.terminal_url + if self.user_id is not None: + result['UserId'] = self.user_id + if self.user_name is not None: + result['UserName'] = self.user_name + if self.user_vpc is not None: + result['UserVpc'] = self.user_vpc.to_map() + if self.web_ideurl is not None: + result['WebIDEUrl'] = self.web_ideurl + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id + if self.workspace_name is not None: + result['WorkspaceName'] = self.workspace_name + if self.workspace_source is not None: + result['WorkspaceSource'] = self.workspace_source + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('AcceleratorType') is not None: + self.accelerator_type = m.get('AcceleratorType') + if m.get('Accessibility') is not None: + self.accessibility = m.get('Accessibility') + if m.get('AccumulatedRunningTimeInMs') is not None: + self.accumulated_running_time_in_ms = m.get('AccumulatedRunningTimeInMs') + self.cloud_disks = [] + if m.get('CloudDisks') is not None: + for k in m.get('CloudDisks'): + temp_model = ListInstancesResponseBodyInstancesCloudDisks() + self.cloud_disks.append(temp_model.from_map(k)) + self.datasets = [] + if m.get('Datasets') is not None: + for k in m.get('Datasets'): + temp_model = ListInstancesResponseBodyInstancesDatasets() + self.datasets.append(temp_model.from_map(k)) + if m.get('Driver') is not None: + self.driver = m.get('Driver') + if m.get('EcsSpec') is not None: + self.ecs_spec = m.get('EcsSpec') + if m.get('EnvironmentVariables') is not None: + self.environment_variables = m.get('EnvironmentVariables') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + if m.get('IdleInstanceCuller') is not None: + temp_model = ListInstancesResponseBodyInstancesIdleInstanceCuller() + self.idle_instance_culler = temp_model.from_map(m['IdleInstanceCuller']) + if m.get('ImageId') is not None: + self.image_id = m.get('ImageId') + if m.get('ImageName') is not None: + self.image_name = m.get('ImageName') + if m.get('ImageUrl') is not None: + self.image_url = m.get('ImageUrl') + if m.get('InstanceId') is not None: + self.instance_id = m.get('InstanceId') + if m.get('InstanceName') is not None: + self.instance_name = m.get('InstanceName') + if m.get('InstanceShutdownTimer') is not None: + temp_model = ListInstancesResponseBodyInstancesInstanceShutdownTimer() + self.instance_shutdown_timer = temp_model.from_map(m['InstanceShutdownTimer']) + self.instance_snapshot_list = [] + if m.get('InstanceSnapshotList') is not None: + for k in m.get('InstanceSnapshotList'): + temp_model = ListInstancesResponseBodyInstancesInstanceSnapshotList() + self.instance_snapshot_list.append(temp_model.from_map(k)) + if m.get('InstanceUrl') is not None: + self.instance_url = m.get('InstanceUrl') + if m.get('JupyterlabUrl') is not None: + self.jupyterlab_url = m.get('JupyterlabUrl') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = ListInstancesResponseBodyInstancesLabels() + self.labels.append(temp_model.from_map(k)) + if m.get('LatestSnapshot') is not None: + temp_model = ListInstancesResponseBodyInstancesLatestSnapshot() + self.latest_snapshot = temp_model.from_map(m['LatestSnapshot']) + if m.get('PaymentType') is not None: + self.payment_type = m.get('PaymentType') + if m.get('Priority') is not None: + self.priority = m.get('Priority') + if m.get('ReasonCode') is not None: + self.reason_code = m.get('ReasonCode') + if m.get('ReasonMessage') is not None: + self.reason_message = m.get('ReasonMessage') + if m.get('RequestedResource') is not None: + temp_model = ListInstancesResponseBodyInstancesRequestedResource() + self.requested_resource = temp_model.from_map(m['RequestedResource']) + if m.get('ResourceId') is not None: + self.resource_id = m.get('ResourceId') + if m.get('ResourceName') is not None: + self.resource_name = m.get('ResourceName') + if m.get('Status') is not None: + self.status = m.get('Status') + if m.get('TerminalUrl') is not None: + self.terminal_url = m.get('TerminalUrl') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + if m.get('UserName') is not None: + self.user_name = m.get('UserName') + if m.get('UserVpc') is not None: + temp_model = ListInstancesResponseBodyInstancesUserVpc() + self.user_vpc = temp_model.from_map(m['UserVpc']) + if m.get('WebIDEUrl') is not None: + self.web_ideurl = m.get('WebIDEUrl') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') + if m.get('WorkspaceName') is not None: + self.workspace_name = m.get('WorkspaceName') + if m.get('WorkspaceSource') is not None: + self.workspace_source = m.get('WorkspaceSource') + return self + + +class ListInstancesResponseBody(TeaModel): + def __init__( + self, + code: str = None, + http_status_code: int = None, + instances: List[ListInstancesResponseBodyInstances] = None, + message: str = None, + request_id: str = None, + success: bool = None, + total_count: int = None, + ): + self.code = code + self.http_status_code = http_status_code + self.instances = instances + self.message = message + self.request_id = request_id + self.success = success + self.total_count = total_count + + def validate(self): + if self.instances: + for k in self.instances: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.code is not None: + result['Code'] = self.code + if self.http_status_code is not None: + result['HttpStatusCode'] = self.http_status_code + result['Instances'] = [] + if self.instances is not None: + for k in self.instances: + result['Instances'].append(k.to_map() if k else None) + if self.message is not None: + result['Message'] = self.message + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.success is not None: + result['Success'] = self.success + if self.total_count is not None: + result['TotalCount'] = self.total_count + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Code') is not None: + self.code = m.get('Code') + if m.get('HttpStatusCode') is not None: + self.http_status_code = m.get('HttpStatusCode') + self.instances = [] + if m.get('Instances') is not None: + for k in m.get('Instances'): + temp_model = ListInstancesResponseBodyInstances() + self.instances.append(temp_model.from_map(k)) + if m.get('Message') is not None: + self.message = m.get('Message') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('Success') is not None: + self.success = m.get('Success') + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') + return self + + +class ListInstancesResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: ListInstancesResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = ListInstancesResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class StartInstanceResponseBody(TeaModel): + def __init__( + self, + code: str = None, + http_status_code: int = None, + instance_id: str = None, + message: str = None, + request_id: str = None, + success: bool = None, + ): + self.code = code + self.http_status_code = http_status_code + self.instance_id = instance_id + self.message = message + self.request_id = request_id + self.success = success + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.code is not None: + result['Code'] = self.code + if self.http_status_code is not None: + result['HttpStatusCode'] = self.http_status_code + if self.instance_id is not None: + result['InstanceId'] = self.instance_id + if self.message is not None: + result['Message'] = self.message + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.success is not None: + result['Success'] = self.success + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Code') is not None: + self.code = m.get('Code') + if m.get('HttpStatusCode') is not None: + self.http_status_code = m.get('HttpStatusCode') + if m.get('InstanceId') is not None: + self.instance_id = m.get('InstanceId') + if m.get('Message') is not None: + self.message = m.get('Message') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('Success') is not None: + self.success = m.get('Success') + return self + + +class StartInstanceResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: StartInstanceResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = StartInstanceResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class StopInstanceRequest(TeaModel): + def __init__( + self, + save_image: bool = None, + ): + self.save_image = save_image + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.save_image is not None: + result['SaveImage'] = self.save_image + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('SaveImage') is not None: + self.save_image = m.get('SaveImage') + return self + + +class StopInstanceResponseBody(TeaModel): + def __init__( + self, + code: str = None, + http_status_code: int = None, + instance_id: str = None, + message: str = None, + request_id: str = None, + success: bool = None, + ): + self.code = code + self.http_status_code = http_status_code + self.instance_id = instance_id + self.message = message + self.request_id = request_id + self.success = success + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.code is not None: + result['Code'] = self.code + if self.http_status_code is not None: + result['HttpStatusCode'] = self.http_status_code + if self.instance_id is not None: + result['InstanceId'] = self.instance_id + if self.message is not None: + result['Message'] = self.message + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.success is not None: + result['Success'] = self.success + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Code') is not None: + self.code = m.get('Code') + if m.get('HttpStatusCode') is not None: + self.http_status_code = m.get('HttpStatusCode') + if m.get('InstanceId') is not None: + self.instance_id = m.get('InstanceId') + if m.get('Message') is not None: + self.message = m.get('Message') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('Success') is not None: + self.success = m.get('Success') + return self + + +class StopInstanceResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: StopInstanceResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = StopInstanceResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class UpdateInstanceRequestCloudDisks(TeaModel): + def __init__( + self, + capacity: str = None, + sub_type: str = None, + ): + self.capacity = capacity + self.sub_type = sub_type + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.capacity is not None: + result['Capacity'] = self.capacity + if self.sub_type is not None: + result['SubType'] = self.sub_type + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Capacity') is not None: + self.capacity = m.get('Capacity') + if m.get('SubType') is not None: + self.sub_type = m.get('SubType') + return self + + +class UpdateInstanceRequestDatasets(TeaModel): + def __init__( + self, + dataset_id: str = None, + mount_path: str = None, + ): + self.dataset_id = dataset_id + self.mount_path = mount_path + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.dataset_id is not None: + result['DatasetId'] = self.dataset_id + if self.mount_path is not None: + result['MountPath'] = self.mount_path + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('DatasetId') is not None: + self.dataset_id = m.get('DatasetId') + if m.get('MountPath') is not None: + self.mount_path = m.get('MountPath') + return self + + +class UpdateInstanceRequestRequestedResource(TeaModel): + def __init__( + self, + cpu: str = None, + gpu: str = None, + gputype: str = None, + memory: str = None, + shared_memory: str = None, + ): + self.cpu = cpu + self.gpu = gpu + self.gputype = gputype + self.memory = memory + self.shared_memory = shared_memory + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.cpu is not None: + result['CPU'] = self.cpu + if self.gpu is not None: + result['GPU'] = self.gpu + if self.gputype is not None: + result['GPUType'] = self.gputype + if self.memory is not None: + result['Memory'] = self.memory + if self.shared_memory is not None: + result['SharedMemory'] = self.shared_memory + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('CPU') is not None: + self.cpu = m.get('CPU') + if m.get('GPU') is not None: + self.gpu = m.get('GPU') + if m.get('GPUType') is not None: + self.gputype = m.get('GPUType') + if m.get('Memory') is not None: + self.memory = m.get('Memory') + if m.get('SharedMemory') is not None: + self.shared_memory = m.get('SharedMemory') + return self + + +class UpdateInstanceRequestUserVpc(TeaModel): + def __init__( + self, + default_route: str = None, + extended_cidrs: List[str] = None, + forward_infos: List[ForwardInfo] = None, + security_group_id: str = None, + v_switch_id: str = None, + vpc_id: str = None, + ): + self.default_route = default_route + self.extended_cidrs = extended_cidrs + self.forward_infos = forward_infos + self.security_group_id = security_group_id + self.v_switch_id = v_switch_id + self.vpc_id = vpc_id + + def validate(self): + if self.forward_infos: + for k in self.forward_infos: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.default_route is not None: + result['DefaultRoute'] = self.default_route + if self.extended_cidrs is not None: + result['ExtendedCIDRs'] = self.extended_cidrs + result['ForwardInfos'] = [] + if self.forward_infos is not None: + for k in self.forward_infos: + result['ForwardInfos'].append(k.to_map() if k else None) + if self.security_group_id is not None: + result['SecurityGroupId'] = self.security_group_id + if self.v_switch_id is not None: + result['VSwitchId'] = self.v_switch_id + if self.vpc_id is not None: + result['VpcId'] = self.vpc_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('DefaultRoute') is not None: + self.default_route = m.get('DefaultRoute') + if m.get('ExtendedCIDRs') is not None: + self.extended_cidrs = m.get('ExtendedCIDRs') + self.forward_infos = [] + if m.get('ForwardInfos') is not None: + for k in m.get('ForwardInfos'): + temp_model = ForwardInfo() + self.forward_infos.append(temp_model.from_map(k)) + if m.get('SecurityGroupId') is not None: + self.security_group_id = m.get('SecurityGroupId') + if m.get('VSwitchId') is not None: + self.v_switch_id = m.get('VSwitchId') + if m.get('VpcId') is not None: + self.vpc_id = m.get('VpcId') + return self + + +class UpdateInstanceRequest(TeaModel): + def __init__( + self, + accessibility: str = None, + cloud_disks: List[UpdateInstanceRequestCloudDisks] = None, + datasets: List[UpdateInstanceRequestDatasets] = None, + disassociate_datasets: bool = None, + disassociate_driver: bool = None, + disassociate_forward_infos: bool = None, + disassociate_vpc: bool = None, + driver: str = None, + ecs_spec: str = None, + image_id: str = None, + image_url: str = None, + instance_name: str = None, + priority: int = None, + requested_resource: UpdateInstanceRequestRequestedResource = None, + user_id: str = None, + user_vpc: UpdateInstanceRequestUserVpc = None, + workspace_source: str = None, + ): + self.accessibility = accessibility + self.cloud_disks = cloud_disks + self.datasets = datasets + self.disassociate_datasets = disassociate_datasets + self.disassociate_driver = disassociate_driver + self.disassociate_forward_infos = disassociate_forward_infos + self.disassociate_vpc = disassociate_vpc + self.driver = driver + self.ecs_spec = ecs_spec + self.image_id = image_id + self.image_url = image_url + self.instance_name = instance_name + self.priority = priority + self.requested_resource = requested_resource + self.user_id = user_id + self.user_vpc = user_vpc + self.workspace_source = workspace_source + + def validate(self): + if self.cloud_disks: + for k in self.cloud_disks: + if k: + k.validate() + if self.datasets: + for k in self.datasets: + if k: + k.validate() + if self.requested_resource: + self.requested_resource.validate() + if self.user_vpc: + self.user_vpc.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.accessibility is not None: + result['Accessibility'] = self.accessibility + result['CloudDisks'] = [] + if self.cloud_disks is not None: + for k in self.cloud_disks: + result['CloudDisks'].append(k.to_map() if k else None) + result['Datasets'] = [] + if self.datasets is not None: + for k in self.datasets: + result['Datasets'].append(k.to_map() if k else None) + if self.disassociate_datasets is not None: + result['DisassociateDatasets'] = self.disassociate_datasets + if self.disassociate_driver is not None: + result['DisassociateDriver'] = self.disassociate_driver + if self.disassociate_forward_infos is not None: + result['DisassociateForwardInfos'] = self.disassociate_forward_infos + if self.disassociate_vpc is not None: + result['DisassociateVpc'] = self.disassociate_vpc + if self.driver is not None: + result['Driver'] = self.driver + if self.ecs_spec is not None: + result['EcsSpec'] = self.ecs_spec + if self.image_id is not None: + result['ImageId'] = self.image_id + if self.image_url is not None: + result['ImageUrl'] = self.image_url + if self.instance_name is not None: + result['InstanceName'] = self.instance_name + if self.priority is not None: + result['Priority'] = self.priority + if self.requested_resource is not None: + result['RequestedResource'] = self.requested_resource.to_map() + if self.user_id is not None: + result['UserId'] = self.user_id + if self.user_vpc is not None: + result['UserVpc'] = self.user_vpc.to_map() + if self.workspace_source is not None: + result['WorkspaceSource'] = self.workspace_source + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Accessibility') is not None: + self.accessibility = m.get('Accessibility') + self.cloud_disks = [] + if m.get('CloudDisks') is not None: + for k in m.get('CloudDisks'): + temp_model = UpdateInstanceRequestCloudDisks() + self.cloud_disks.append(temp_model.from_map(k)) + self.datasets = [] + if m.get('Datasets') is not None: + for k in m.get('Datasets'): + temp_model = UpdateInstanceRequestDatasets() + self.datasets.append(temp_model.from_map(k)) + if m.get('DisassociateDatasets') is not None: + self.disassociate_datasets = m.get('DisassociateDatasets') + if m.get('DisassociateDriver') is not None: + self.disassociate_driver = m.get('DisassociateDriver') + if m.get('DisassociateForwardInfos') is not None: + self.disassociate_forward_infos = m.get('DisassociateForwardInfos') + if m.get('DisassociateVpc') is not None: + self.disassociate_vpc = m.get('DisassociateVpc') + if m.get('Driver') is not None: + self.driver = m.get('Driver') + if m.get('EcsSpec') is not None: + self.ecs_spec = m.get('EcsSpec') + if m.get('ImageId') is not None: + self.image_id = m.get('ImageId') + if m.get('ImageUrl') is not None: + self.image_url = m.get('ImageUrl') + if m.get('InstanceName') is not None: + self.instance_name = m.get('InstanceName') + if m.get('Priority') is not None: + self.priority = m.get('Priority') + if m.get('RequestedResource') is not None: + temp_model = UpdateInstanceRequestRequestedResource() + self.requested_resource = temp_model.from_map(m['RequestedResource']) + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + if m.get('UserVpc') is not None: + temp_model = UpdateInstanceRequestUserVpc() + self.user_vpc = temp_model.from_map(m['UserVpc']) + if m.get('WorkspaceSource') is not None: + self.workspace_source = m.get('WorkspaceSource') + return self + + +class UpdateInstanceResponseBody(TeaModel): + def __init__( + self, + code: str = None, + http_status_code: int = None, + instance_id: str = None, + message: str = None, + request_id: str = None, + success: bool = None, + ): + self.code = code + self.http_status_code = http_status_code + self.instance_id = instance_id + self.message = message + self.request_id = request_id + self.success = success + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.code is not None: + result['Code'] = self.code + if self.http_status_code is not None: + result['HttpStatusCode'] = self.http_status_code + if self.instance_id is not None: + result['InstanceId'] = self.instance_id + if self.message is not None: + result['Message'] = self.message + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.success is not None: + result['Success'] = self.success + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Code') is not None: + self.code = m.get('Code') + if m.get('HttpStatusCode') is not None: + self.http_status_code = m.get('HttpStatusCode') + if m.get('InstanceId') is not None: + self.instance_id = m.get('InstanceId') + if m.get('Message') is not None: + self.message = m.get('Message') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('Success') is not None: + self.success = m.get('Success') + return self + + +class UpdateInstanceResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: UpdateInstanceResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = UpdateInstanceResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + diff --git a/pai/toolkit/config.py b/pai/toolkit/config.py index ca1b79d..acec326 100644 --- a/pai/toolkit/config.py +++ b/pai/toolkit/config.py @@ -634,6 +634,63 @@ def prompt_for_config_writing( ) +def prompt_config_with_default_dsw_role(user_profile: UserProfile): + print( + localized_text( + "The current DSW instance is bound to the default PAI DSW role," + " automatically utilizes the instance's workspace and OSS Bucket configurations.", + "当前DSW实例绑定PAI DSW默认角色,自动使用实例的工作空间和OSS Bucket配置", + ) + ) + instance_id = os.environ.get("DSW_INSTANCE_ID") + if not instance_id: + raise RuntimeError( + "Not found PAI DSW instance id from environment variable: DSW_INSTANCE_ID" + ) + instance_info = user_profile.get_instance_info(instance_id=instance_id) + workspace_id = instance_info["WorkspaceId"] + workspace_name = instance_info["WorkspaceName"] + user_id = instance_info["UserId"] + roles = user_profile.get_roles_in_workspace(workspace_id, user_id) + role_info = ", ".join(roles) + print_highlight( + localized_text( + "Current workspace configuration:", + "当前实例的工作空间信息:", + ) + ) + print_highlight( + "WorkspaceName: {}\nWorkspaceId: {}\nRoles: {}".format( + workspace_name, + workspace_id, + role_info, + ) + ) + + default_storage_uri = user_profile.get_default_oss_storage_uri( + workspace_id=workspace_id, + ) + + if not default_storage_uri: + print_warning( + localized_text( + "The current DSW instance is configured to use the PAI DSW default role. " + "The STS temporary credentials generated by the default role only support accessing " + 'the OSS Bucket of the "default workspace storage.". Please reference the document to ' + "configure the default storage: " + "https://help.aliyun.com/zh/pai/user-guide/manage-workspaces#section-afd-ntr-nwh", + '当前DSW实例配置使用PAI DSW默认角色。默认角色产生的STS临时凭证仅支持访问"工作空间默认存储"的OSS Bucket。' + "请参考帮助文档配置工作空间的默认存储:" + "https://help.aliyun.com/zh/pai/user-guide/manage-workspaces#section-afd-ntr-nwh", + ) + ) + bucket_name, endpoint = None, None + else: + bucket_name = OssUriObj(default_storage_uri).bucket_name + endpoint = f"oss-{user_profile.region_id}-internal.aliyuncs.com" + return workspace_id, bucket_name, endpoint + + def run(): """ The flow of pai config. @@ -654,12 +711,19 @@ def run(): # Ask for Account profile user_profile = prompt_for_credential() - # Ask for workspace - workspace_id = prompt_for_workspace(user_profile=user_profile) - # Ask for OSS Bucket - bucket_name, bucket_endpoint = prompt_for_oss_bucket( - user_profile=user_profile, workspace_id=workspace_id - ) + if user_profile.is_dsw_default_role(): + ( + workspace_id, + bucket_name, + bucket_endpoint, + ) = prompt_config_with_default_dsw_role(user_profile=user_profile) + else: + # Ask for workspace + workspace_id = prompt_for_workspace(user_profile=user_profile) + # Ask for OSS Bucket + bucket_name, bucket_endpoint = prompt_for_oss_bucket( + user_profile=user_profile, workspace_id=workspace_id + ) # Ask for config writing prompt_for_config_writing( diff --git a/pai/toolkit/helper/utils.py b/pai/toolkit/helper/utils.py index 474fc0d..6722f21 100644 --- a/pai/toolkit/helper/utils.py +++ b/pai/toolkit/helper/utils.py @@ -15,7 +15,7 @@ import locale import os import re -from typing import List +from typing import Any, Dict, List, Optional import oss2 from alibabacloud_credentials.client import Client as CredentialClient @@ -39,6 +39,7 @@ from ...common.logging import get_logger from ...common.oss_utils import CredentialProviderWrapper, OssUriObj from ...common.utils import make_list_resource_iterator +from ...libs.alibabacloud_pai_dsw20220101.client import Client as DswClient logger = get_logger(__name__) @@ -47,6 +48,13 @@ OSS_NAME_PATTERN = re.compile(pattern="^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$") ZH_CN_LOCAL = "zh_CN" +# RoleARN pattern for AssumedRole CallerIdentity +ASSUMED_ROLE_ARN_PATTERN = re.compile(r"acs:ram::\d+:assumed-role/([^/]+)/.*") + +# DSW Notebook Default Role Name: +PAI_DSW_DEFAULT_ROLE_NAME = "aliyunpaidswdefaultrole" + + DEFAULT_PRODUCT_RAM_ROLE_NAMES = [ "AliyunODPSPAIDefaultRole", "AliyunPAIAccessingOSSRole", @@ -125,6 +133,23 @@ def _get_caller_identity(self) -> CallerIdentity: .body ) + def is_dsw_default_role(self) -> bool: + if self._caller_identify.identity_type != CallerIdentityType.AssumedRoleUser: + return False + m = ASSUMED_ROLE_ARN_PATTERN.match(self._caller_identify.arn) + return m and m.group(1).lower() == PAI_DSW_DEFAULT_ROLE_NAME + + def get_acs_dsw_client(self) -> DswClient: + return ClientFactory.create_client( + service_name=ServiceName.PAI_DSW, + credential_client=self._get_credential_client(), + region_id=self.region_id, + ) + + def get_instance_info(self, instance_id: str) -> Dict[str, Any]: + dsw_client = self.get_acs_dsw_client() + return dsw_client.get_instance(instance_id).body.to_map() + def get_credential(self): return self._credential_client.get_access_key_id() @@ -250,8 +275,11 @@ def set_default_oss_storage( configs = {WorkspaceConfigKeys.DEFAULT_OSS_STORAGE_URI: oss_uri} workspace_api.update_configs(workspace_id, configs=configs) - def get_roles_in_workspace(self, workspace_id) -> List[str]: + def get_roles_in_workspace( + self, workspace_id, user_id: Optional[str] = None + ) -> List[str]: workspace_api = self.get_workspace_api() + user_id = user_id or self.user_id member_info = next( ( mem @@ -259,7 +287,7 @@ def get_roles_in_workspace(self, workspace_id) -> List[str]: workspace_api.list_members, workspace_id=workspace_id, ) - if mem["UserId"] == self.user_id + if mem["UserId"] == user_id ), None, ) From d4238edc228b9ae2b19d63f300991e73652b43b5 Mon Sep 17 00:00:00 2001 From: LiangQuan Date: Mon, 3 Jun 2024 17:56:03 +0800 Subject: [PATCH 29/59] release 0.4.7.post0 (#23) --- pai/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pai/version.py b/pai/version.py index 7b413b3..023d357 100644 --- a/pai/version.py +++ b/pai/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -VERSION = "0.4.8.dev0" +VERSION = "0.4.7.post0" From 50ad53b69bc3c1ca6ae5a0d635db0e36d53693ab Mon Sep 17 00:00:00 2001 From: LiangQuan Date: Tue, 11 Jun 2024 16:45:30 +0800 Subject: [PATCH 30/59] feat: add network parameter for session (#24) * feat: pai.tookkit.config supports vpc network * feat: add network parameter for session * feat: add `Network` enum --- pai/api/api_container.py | 28 ++++++++++++++++++++++++++-- pai/api/client_factory.py | 16 ++++++++++++++-- pai/common/consts.py | 23 ++++++++++++++++++++++- pai/session.py | 18 ++++++++++++++++-- pai/toolkit/config.py | 12 ++++++------ pai/toolkit/helper/utils.py | 17 ++++++++++++++++- 6 files changed, 100 insertions(+), 14 deletions(-) diff --git a/pai/api/api_container.py b/pai/api/api_container.py index 6832fc6..3faace8 100644 --- a/pai/api/api_container.py +++ b/pai/api/api_container.py @@ -11,11 +11,13 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - +from typing import Optional, Union from alibabacloud_credentials.client import Client as CredentialClient from alibabacloud_sts20150401.client import Client as StsClient +from ..common.consts import DEFAULT_NETWORK_TYPE, PAI_VPC_ENDPOINT, Network +from ..common.utils import is_domain_connectable from .algorithm import AlgorithmAPI from .base import PAIRestResourceTypes, ServiceName, WorkspaceScopedResourceAPI from .client_factory import ClientFactory @@ -57,11 +59,32 @@ class ResourceAPIsContainerMixin(object): _region_id = None _workspace_id = None - def __init__(self, header=None, runtime=None): + def __init__( + self, header=None, runtime=None, network: Optional[Union[str, Network]] = None + ): + """Initialize ResourceAPIsContainerMixin. + + Args: + header: Header for API request. + runtime: Runtime for API request. + network: Network type used to connect to PAI services. + """ self.header = header self.runtime = runtime self.api_container = dict() self.acs_client_container = dict() + if network: + self.network = ( + Network.from_string(network) if isinstance(network, str) else network + ) + elif DEFAULT_NETWORK_TYPE: + self.network = Network.from_string(DEFAULT_NETWORK_TYPE) + else: + self.network = ( + Network.VPC + if is_domain_connectable(PAI_VPC_ENDPOINT) + else Network.PUBLIC + ) def _acs_credential_client(self): if self._credential_client: @@ -76,6 +99,7 @@ def _get_acs_client(self, service_name): service_name=service_name, credential_client=self._acs_credential_client(), region_id=self._region_id, + network=self.network, ) self.acs_client_container[service_name] = acs_client return acs_client diff --git a/pai/api/client_factory.py b/pai/api/client_factory.py index 73b5483..07d3dd1 100644 --- a/pai/api/client_factory.py +++ b/pai/api/client_factory.py @@ -14,10 +14,13 @@ from __future__ import absolute_import +from typing import Optional + from alibabacloud_credentials.client import Client as CredentialClient from alibabacloud_sts20150401.client import Client as StsClient from alibabacloud_tea_openapi.models import Config +from ..common.consts import Network from ..common.logging import get_logger from ..common.utils import http_user_agent from ..libs.alibabacloud_aiworkspace20210204.client import Client as WorkspaceClient @@ -54,16 +57,19 @@ def create_client( service_name, region_id: str, credential_client: CredentialClient, + network: Optional[Network] = None, **kwargs, ): """Create an API client which is responsible to interacted with the Alibaba Cloud service.""" + config = Config( region_id=region_id, credential=credential_client, endpoint=cls.get_endpoint( service_name=service_name, region_id=region_id, + network=network, ), signature_algorithm="v2", user_agent=http_user_agent(), @@ -73,9 +79,15 @@ def create_client( return client @classmethod - def get_endpoint(cls, service_name: str, region_id: str) -> str: + def get_endpoint( + cls, service_name: str, region_id: str, network: Optional[Network] = None + ) -> str: """Get the endpoint for the service client.""" if not region_id: raise ValueError("Please provide region_id to get the endpoint.") - return DEFAULT_SERVICE_ENDPOINT_PATTERN.format(service_name, region_id) + if network and network != Network.PUBLIC: + subdomain = f"{service_name}-{network.value.lower()}" + else: + subdomain = service_name + return DEFAULT_SERVICE_ENDPOINT_PATTERN.format(subdomain, region_id) diff --git a/pai/common/consts.py b/pai/common/consts.py index b4dc8d9..b2cce95 100644 --- a/pai/common/consts.py +++ b/pai/common/consts.py @@ -11,7 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - +import enum import os # Default path for pai config file @@ -19,6 +19,27 @@ "PAI_CONFIG_PATH", os.path.join(os.path.expanduser("~"), ".pai", "config.json") ) +# Default network type used to connect to PAI services +DEFAULT_NETWORK_TYPE = os.environ.get("PAI_NETWORK_TYPE", None) + +# PAI VPC endpoint +PAI_VPC_ENDPOINT = "pai-vpc.aliyuncs.com" + + +class Network(enum.Enum): + VPC = "VPC" + PUBLIC = "PUBLIC" + + @classmethod + def from_string(cls, s: str) -> "Network": + try: + return cls[s.upper()] + except KeyError: + raise ValueError( + "Invalid network type: %s, supported types are: %s" + % (s, ", ".join(cls.__members__.keys())) + ) + class JobType(object): """PAI DLCJob/TrainingJob type.""" diff --git a/pai/session.py b/pai/session.py index add6a07..7db682d 100644 --- a/pai/session.py +++ b/pai/session.py @@ -25,7 +25,7 @@ from alibabacloud_credentials.utils import auth_constant from .api.api_container import ResourceAPIsContainerMixin -from .common.consts import DEFAULT_CONFIG_PATH +from .common.consts import DEFAULT_CONFIG_PATH, Network from .common.logging import get_logger from .common.oss_utils import CredentialProviderWrapper, OssUriObj from .common.utils import is_domain_connectable, make_list_resource_iterator @@ -60,6 +60,7 @@ def setup_default_session( oss_bucket_name: Optional[str] = None, oss_endpoint: Optional[str] = None, workspace_id: Optional[Union[str, int]] = None, + network: Optional[Union[str, Network]] = None, **kwargs, ) -> "Session": """Set up the default session used in the program. @@ -81,6 +82,11 @@ def setup_default_session( oss_bucket_name (str, optional): The name of the OSS bucket used in the session. oss_endpoint (str, optional): The endpoint for the OSS bucket. + network (Union[str, Network], optional): The network to use for the connection. + supported values are "VPC" and "PUBLIC". If provided, this value will be used as-is. + Otherwise, the code will first check for an environment variable PAI_NETWORK_TYPE. + If that is not set and the VPC endpoint is available, it will be used. + As a last resort, if all else fails, the PUBLIC endpoint will be used. **kwargs: Returns: @@ -114,6 +120,7 @@ def setup_default_session( oss_bucket_name = oss_bucket_name or default_session.oss_bucket_name oss_endpoint = oss_endpoint or default_session.oss_endpoint credential_config = credential_config or default_session.credential_config + network = network or default_session.network session = Session( region_id=region_id, @@ -121,6 +128,7 @@ def setup_default_session( oss_bucket_name=oss_bucket_name, oss_endpoint=oss_endpoint, workspace_id=workspace_id, + network=network, **kwargs, ) @@ -208,7 +216,13 @@ def __init__( self._oss_endpoint = oss_endpoint header = kwargs.pop("header", None) - super(Session, self).__init__(header=header) + network = kwargs.pop("network", None) + runtime = kwargs.pop("runtime", None) + if kwargs: + logger.warning( + "Unused arguments found in session initialization: %s", kwargs + ) + super(Session, self).__init__(header=header, network=network, runtime=runtime) @property def region_id(self) -> str: diff --git a/pai/toolkit/config.py b/pai/toolkit/config.py index acec326..538ad97 100644 --- a/pai/toolkit/config.py +++ b/pai/toolkit/config.py @@ -674,13 +674,13 @@ def prompt_config_with_default_dsw_role(user_profile: UserProfile): if not default_storage_uri: print_warning( localized_text( - "The current DSW instance is configured to use the PAI DSW default role. " - "The STS temporary credentials generated by the default role only support accessing " - 'the OSS Bucket of the "default workspace storage.". Please reference the document to ' - "configure the default storage: " + "WARNING: The STS credential generated by the default ROLE only support accessing " + "the default OSS Bucket storage of the workspace.\n" + "It is not configured for the current workspace, please " + "reference the document to configure the default OSS Bucket storage: \n" "https://help.aliyun.com/zh/pai/user-guide/manage-workspaces#section-afd-ntr-nwh", - '当前DSW实例配置使用PAI DSW默认角色。默认角色产生的STS临时凭证仅支持访问"工作空间默认存储"的OSS Bucket。' - "请参考帮助文档配置工作空间的默认存储:" + '警告:默认角色产生的STS凭证仅支持访问"工作空间默认存储"的OSS Bucket。\n' + "当前工作空间没有配置默认OSS Bucket存储,请参考帮助文档进行配置:\n" "https://help.aliyun.com/zh/pai/user-guide/manage-workspaces#section-afd-ntr-nwh", ) ) diff --git a/pai/toolkit/helper/utils.py b/pai/toolkit/helper/utils.py index 6722f21..2089ccb 100644 --- a/pai/toolkit/helper/utils.py +++ b/pai/toolkit/helper/utils.py @@ -36,9 +36,10 @@ from ...api.base import ServiceName from ...api.client_factory import ClientFactory from ...api.workspace import WorkspaceAPI, WorkspaceConfigKeys +from ...common.consts import DEFAULT_NETWORK_TYPE, PAI_VPC_ENDPOINT, Network from ...common.logging import get_logger from ...common.oss_utils import CredentialProviderWrapper, OssUriObj -from ...common.utils import make_list_resource_iterator +from ...common.utils import is_domain_connectable, make_list_resource_iterator from ...libs.alibabacloud_pai_dsw20220101.client import Client as DswClient logger = get_logger(__name__) @@ -104,6 +105,15 @@ def __init__( ): self.region_id = region_id self.credential_config = credential_config + + if DEFAULT_NETWORK_TYPE: + self.network = Network.from_string(DEFAULT_NETWORK_TYPE) + else: + self.network = ( + Network.VPC + if is_domain_connectable(PAI_VPC_ENDPOINT) + else Network.PUBLIC + ) self._caller_identify = self._get_caller_identity() def _get_credential_client(self): @@ -127,6 +137,9 @@ def _get_caller_identity(self) -> CallerIdentity: config=open_api_models.Config( credential=self._get_credential_client(), region_id=self.region_id, + network=None + if self.network == Network.PUBLIC + else self.network.value.lower(), ) ) .get_caller_identity() @@ -144,6 +157,7 @@ def get_acs_dsw_client(self) -> DswClient: service_name=ServiceName.PAI_DSW, credential_client=self._get_credential_client(), region_id=self.region_id, + network=self.network, ) def get_instance_info(self, instance_id: str) -> Dict[str, Any]: @@ -240,6 +254,7 @@ def get_workspace_api(self) -> WorkspaceAPI: service_name=ServiceName.PAI_WORKSPACE, credential_client=self._get_credential_client(), region_id=self.region_id, + network=self.network, ) return WorkspaceAPI( From 130c99bc81e9e13d478364670385ee503c4d1135 Mon Sep 17 00:00:00 2001 From: LiangQuan Date: Fri, 28 Jun 2024 12:10:26 +0800 Subject: [PATCH 31/59] Refactor training job subimit. (#28) * refactor model training submitter * upgrade unit-test python version * remove patch_model_deploy in integration test * update to development version * simple read_mc_table test case * fix `has_docker` decorator --- README.md | 2 +- README_EN.md | 2 +- docs/source/quick-tour/installation.rst | 2 +- noxfile.py | 7 +- pai/api/entity_base.py | 70 -- pai/api/training_job.py | 1 + pai/common/configs.py | 55 - pai/common/utils.py | 41 +- pai/estimator.py | 1074 +++-------------- pai/experiment.py | 42 +- pai/job/__init__.py | 48 + pai/job/_local_training_job.py | 293 +++++ pai/job/_training_job.py | 872 +++++++++++++ pai/model.py | 269 +++-- pai/processor.py | 417 ++----- pai/schema/__init__.py | 13 - pai/schema/base.py | 107 -- pai/schema/training_job_schema.py | 113 -- pai/version.py | 2 +- requirements/requirements.txt | 5 +- requirements/test-requirements.txt | 1 + setup.py | 2 +- tests/integration/__init__.py | 23 - tests/integration/test_estimator.py | 33 +- tests/integration/test_experiment.py | 16 +- tests/integration/test_model/__init__.py | 0 .../{ => test_model}/test_model.py | 6 +- tests/integration/test_processor.py | 3 +- .../tests_pipeline/test_pipeline_build.py | 4 +- tests/integration/utils.py | 18 +- tests/test_data/read_mc_table/run.py | 3 +- tests/unit/test_pipeline/test_pipeline.py | 2 +- 32 files changed, 1758 insertions(+), 1788 deletions(-) delete mode 100644 pai/api/entity_base.py delete mode 100644 pai/common/configs.py create mode 100644 pai/job/__init__.py create mode 100644 pai/job/_local_training_job.py create mode 100644 pai/job/_training_job.py delete mode 100644 pai/schema/__init__.py delete mode 100644 pai/schema/base.py delete mode 100644 pai/schema/training_job_schema.py create mode 100644 tests/integration/test_model/__init__.py rename tests/integration/{ => test_model}/test_model.py (98%) diff --git a/README.md b/README.md index dd7e92d..57cfbb6 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ PAI Python SDK是阿里云 [机器学习平台 PAI(Platform for Artificial Intel ## 🔧 安装 -使用以下命令安装PAI Python SDK(支持Python版本 \>= 3.6,建议使用Python版本 \>= 3.8): +使用以下命令安装PAI Python SDK(支持Python版本 \>= 3.8): ```shell python -m pip install alipai diff --git a/README_EN.md b/README_EN.md index c5df0e8..851b9f0 100644 --- a/README_EN.md +++ b/README_EN.md @@ -7,7 +7,7 @@ The PAI Python SDK is provided by Alibaba Cloud\'s [Platform for Artificial Inte ## Installation 🔧 -Install the PAI Python SDK using the following command, which supports Python versions \>= 3.6 (it is recommended to use Python \>= 3.8): +Install the PAI Python SDK using the following command, which supports Python versions \>= 3.8 : ```shell python -m pip install alipai diff --git a/docs/source/quick-tour/installation.rst b/docs/source/quick-tour/installation.rst index b48464e..181353f 100644 --- a/docs/source/quick-tour/installation.rst +++ b/docs/source/quick-tour/installation.rst @@ -5,7 +5,7 @@ 安装 ------ -请通过以下命令安装PAI Python SDK(请使用Python>=3.6)。 +请通过以下命令安装PAI Python SDK(请使用Python>=3.8)。 .. parsed-literal:: diff --git a/noxfile.py b/noxfile.py index 686429b..f74aca9 100644 --- a/noxfile.py +++ b/noxfile.py @@ -25,7 +25,7 @@ "PYTHONWARNINGS": "ignore", } -UNIT_TEST_PYTHON_VERSIONS = ["3.6", "3.7", "3.8"] +UNIT_TEST_PYTHON_VERSIONS = ["3.8", "3.9", "3.10"] INTEGRATION_TEST_PYTHON_VERSIONS = ["3.8"] TEST_VENV_BACKEND = os.environ.get("PAI_TEST_VENV_BACKEND", "conda") @@ -54,9 +54,9 @@ def integration(session: Session): pos_args = session.posargs + ["-n", str(os.cpu_count() * 2)] else: pos_args = session.posargs - session.run( "pytest", + "-vv", "--cov-config=.coveragerc", "--cov-append", "--cov-report=html", @@ -77,8 +77,10 @@ def integration(session: Session): def unit(session: Session): """Run unit test.""" install_test_dependencies(session=session) + # run test cases session.run( "pytest", + "-vv", "--cov-config=.coveragerc", "--cov-append", "--cov-report=html", @@ -159,6 +161,7 @@ def notebook(session: Session): session.run( "pytest", + "-vv", "--timeout", "3000", "--nbmake", diff --git a/pai/api/entity_base.py b/pai/api/entity_base.py deleted file mode 100644 index 044ae9a..0000000 --- a/pai/api/entity_base.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright 2023 Alibaba, Inc. or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from typing import Any, Dict, Optional, Type - -from ..schema.base import BaseAPIResourceSchema -from ..session import Session, get_default_session - - -class EntityBaseMixin(object): - - _schema_cls: Type[BaseAPIResourceSchema] - resource_type: str - - def __init__(self, session: Optional[Session] = None, **kwargs) -> None: - super(EntityBaseMixin, self).__init__() - self._session = session or get_default_session() - - @property - def session(self) -> Session: - return self._session - - @classmethod - def from_api_object(cls, obj_dict: Dict[str, Any], session: Session = None): - """Construct an entity representing the API resource from response. - - Args: - session: Session for the instance. - obj_dict: Response in json - - Returns: - An entity representing the resource. - """ - session = session or get_default_session() - return cls._schema_cls(session=session).load(obj_dict) - - def to_api_object(self) -> Dict[str, Any]: - """Convert the current instance to a dictionary representing an API object. - - Returns: - dict: a dictionary representing the API object. - """ - return self._schema_cls().dump(self) - - def patch_from_api_object(self, api_obj: Dict[str, Any]): - if not api_obj: - raise ValueError("REST API object should not be empty.") - - return self._schema_cls(instance=self).load(api_obj) - - def __repr__(self): - return "{}:{}".format(type(self).__name__, self.id) - - def __str__(self) -> str: - return self.__repr__() - - @property - def resource_api(self): - return self._session.get_api_by_resource(self.resource_type) diff --git a/pai/api/training_job.py b/pai/api/training_job.py index 25a2b5d..f8648fa 100644 --- a/pai/api/training_job.py +++ b/pai/api/training_job.py @@ -141,6 +141,7 @@ def create( else: raise ValueError("Please provide instance_type or instance_spec.") + hyperparameters = hyperparameters or dict() hyper_parameters = [ CreateTrainingJobRequestHyperParameters( name=name, diff --git a/pai/common/configs.py b/pai/common/configs.py deleted file mode 100644 index b7b952c..0000000 --- a/pai/common/configs.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright 2024 Alibaba, Inc. or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from typing import List, Optional - - -class UserVpcConfig(object): - """UserVpcConfig is used to give training job access to resources in your VPC.""" - - def __init__( - self, - vpc_id: str, - security_group_id: str, - switch_id: Optional[str] = None, - extended_cidrs: List[str] = None, - ): - """Initialize UserVpcConfig. - - Args: - vpc_id (str): Specifies the ID of the VPC that training job instance - connects to. - security_group_id (str): The ID of the security group that training job - instances belong to. - switch_id (str, optional): The ID of the vSwitch to which the instance - belongs. Defaults to None. - extended_cidrs (List[str], optional): The CIDR blocks configured for the - ENI of the training job instance. If it is not specified, the CIDR block - will be configured as the same as the VPC network segmentation, which - means that the training job instance can access all resources in the - VPC. Defaults to None. - """ - - self.vpc_id = vpc_id - self.security_group_id = security_group_id - self.switch_id = switch_id - self.extended_cidrs = extended_cidrs - - def to_dict(self): - return { - "VpcId": self.vpc_id, - "SecurityGroupId": self.security_group_id, - "SwitchId": self.switch_id, - "ExtendedCIDRs": self.extended_cidrs, - } diff --git a/pai/common/utils.py b/pai/common/utils.py index 4ec74b2..a326c51 100644 --- a/pai/common/utils.py +++ b/pai/common/utils.py @@ -23,6 +23,7 @@ import sys import time import warnings +from datetime import datetime from functools import lru_cache from typing import Callable, Dict, List, Optional, Union @@ -127,12 +128,14 @@ def http_user_agent(user_agent: Optional[Union[Dict, str]] = None) -> str: def is_notebook() -> bool: """Return True if current environment is notebook.""" try: + from IPython import get_ipython + shell = get_ipython().__class__.__name__ for parent_cls in shell.__mro__: if parent_cls.__name__ == "ZMQInteractiveShell": return True return False - except NameError: + except (NameError, ImportError): return False @@ -322,3 +325,39 @@ def print_table(headers: List[str], rows: List[List[str]]): def is_package_available(package_name: str) -> bool: """Check if the package is available in the current environment.""" return True if importlib.util.find_spec(package_name) is not None else False + + +def timestamp(sep: str = "-", utc: bool = False) -> str: + """Return a timestamp with millisecond precision. + + Args: + sep: The separator between date and time. + utc: Whether to use UTC time. + + Returns: + str: A timestamp with millisecond precision. + + """ + if utc: + res = datetime.utcnow().strftime("%Y%m%d-%H%M%S-%f")[:-3] + else: + res = datetime.now().strftime("%Y%m%d-%H%M%S-%f")[:-3] + if sep != "-": + res = res.replace("-", sep) + return res + + +def name_from_base(base_name: str, sep: str = "-") -> str: + """Return a name with base_name and timestamp. + + Args: + base_name: The base name of the returned name. + sep: The separator between base_name and timestamp. + + Returns: + str: A name with base_name and timestamp. + + """ + return "{base_name}{sep}{timestamp}".format( + base_name=base_name, sep=sep, timestamp=timestamp(sep=sep, utc=False) + ) diff --git a/pai/estimator.py b/pai/estimator.py index 1b78e98..a3ee149 100644 --- a/pai/estimator.py +++ b/pai/estimator.py @@ -12,55 +12,38 @@ # See the License for the specific language governing permissions and # limitations under the License. -import distutils.dir_util -import json -import os -import posixpath -import re -import shlex -import shutil -import tempfile -import textwrap -import time import webbrowser from abc import ABCMeta, abstractmethod -from concurrent.futures import ThreadPoolExecutor from datetime import datetime from typing import Any, Dict, List, Optional, Union -from Tea.exceptions import TeaException - -from .api.base import PaginatedResult -from .api.entity_base import EntityBaseMixin -from .common import ProviderAlibabaPAI, git_utils -from .common.configs import UserVpcConfig -from .common.consts import INSTANCE_TYPE_LOCAL_GPU, FileSystemInputScheme, JobType -from .common.docker_utils import ContainerRun, run_container +from .common import git_utils +from .common.consts import FileSystemInputScheme, JobType from .common.logging import get_logger -from .common.oss_utils import OssUriObj, download, is_oss_uri, upload -from .common.utils import ( - is_filesystem_uri, - is_local_run_instance_type, - is_odps_table_uri, - make_list_resource_iterator, - random_str, - retry, - to_plain_text, +from .common.utils import is_local_run_instance_type, make_list_resource_iterator +from .job import ( + AlgorithmSpec, + Channel, + HyperParameterDefinition, + LocalTrainingJob, + TrainingJob, + UriOutput, + _TrainingJobSubmitter, +) +from .job._training_job import ( + DEFAULT_CHECKPOINT_CHANNEL_NAME, + DEFAULT_OUTPUT_MODEL_CHANNEL_NAME, + DEFAULT_TENSORBOARD_CHANNEL_NAME, + ExperimentConfig, + UserVpcConfig, ) -from .exception import UnexpectedStatusException -from .experiment import Experiment, ExperimentConfig from .model import InferenceSpec, Model, ResourceConfig from .predictor import Predictor -from .schema.training_job_schema import TrainingJobSchema from .serializers import SerializerBase from .session import Session, get_default_session logger = get_logger(__name__) -DEFAULT_OUTPUT_MODEL_CHANNEL_NAME = "model" -DEFAULT_CHECKPOINT_CHANNEL_NAME = "checkpoints" -DEFAULT_TENSORBOARD_CHANNEL_NAME = "tensorboard" - class HyperParameterType(object): """Hyperparameter type.""" @@ -184,7 +167,7 @@ def to_input_uri(self): ) -class EstimatorBase(metaclass=ABCMeta): +class EstimatorBase(_TrainingJobSubmitter, metaclass=ABCMeta): """EstimatorBase is the base class for other Estimator classes, such as Estimator. The EstimatorBase class contains common attributes and methods for all estimators, @@ -295,21 +278,22 @@ def __init__( """ self.hyperparameters = hyperparameters or dict() - self.environments = environments - self.requirements = requirements - self.instance_type = instance_type - self.instance_spec = instance_spec - self.resource_id = resource_id - self.instance_count = instance_count if instance_count else 1 - self.max_run_time = max_run_time - self.base_job_name = base_job_name - self.output_path = output_path - self.user_vpc_config = user_vpc_config - self.experiment_config = experiment_config self.checkpoints_path = checkpoints_path self.session = session or get_default_session() - self.labels = labels - self._latest_training_job = None + super().__init__( + base_job_name=base_job_name, + output_path=output_path, + experiment_config=experiment_config, + instance_type=instance_type, + instance_count=instance_count, + resource_id=resource_id, + instance_spec=instance_spec, + user_vpc_config=user_vpc_config, + max_run_time=max_run_time, + environments=environments, + requirements=requirements, + labels=labels, + ) def set_hyperparameters(self, **kwargs): """Set hyperparameters for the training job. @@ -319,11 +303,6 @@ def set_hyperparameters(self, **kwargs): """ self.hyperparameters.update(**kwargs) - @property - def latest_training_job(self): - """Return the latest submitted training job.""" - return self._latest_training_job - def _gen_job_display_name(self, job_name=None): """Generate job display name.""" if job_name: @@ -331,150 +310,12 @@ def _gen_job_display_name(self, job_name=None): ts = datetime.now().strftime("%Y%m%d_%H%M%S") return "{}_{}".format(self.base_job_name or "training_job", ts) - def _get_input_uri(self, item: str): - """Get input uri for training_job from given input.""" - if not isinstance(item, (str, FileSystemInputBase)): - raise ValueError(f"Input data of type {type(item)} is not supported.") - - if isinstance(item, FileSystemInputBase): - input_uri = item.to_input_uri() - elif is_oss_uri(item) or is_filesystem_uri(item) or is_odps_table_uri(item): - input_uri = item - elif os.path.exists(item): - store_path = self.session.get_storage_path_by_category("train_data") - input_uri = upload(item, store_path) - else: - raise ValueError( - "Invalid input data, supported inputs are OSS, NAS, MaxCompute " - "table or local path." - ) - - return input_uri - - def _build_input_data_configs( - self, - inputs: Dict[str, Any] = None, - input_channel_defs: Optional[List[Dict[str, str]]] = None, - ) -> List[Dict[str, str]]: - """Build the input data config for the training job.""" - res = [] - - if input_channel_defs: - remains = set(inputs.keys()) - for channel in input_channel_defs: - channel_name = channel["Name"] - channel_required = channel["Required"] - if channel_name in inputs: - input_uri = self._get_input_uri(inputs[channel_name]) - res.append({"Name": channel_name, "InputUri": input_uri}) - remains.remove(channel_name) - elif channel_required: - raise ValueError( - f"Input channel {channel_name} is required but not provided." - " Please check the input channels definition." - ) - if remains: - raise ValueError( - f"Following input channels={list(remains)} are not defined in input" - " channels definition. Please check the input channels definition." - ) - else: - for name, item in inputs.items(): - input_uri = self._get_input_uri(item) - res.append({"Name": name, "InputUri": input_uri}) - - return res - - def _generate_job_base_output_path(self, job_name: str) -> str: - """Generate the base output path for the training job.""" - bucket = self.session.oss_bucket - bucket_name = bucket.bucket_name - # replace non-alphanumeric character in training job name. - name = to_plain_text(job_name) - - if self.output_path: - return os.path.join(self.output_path, f"{name}_{random_str(6)}") - else: - job_output_path = self.session.get_storage_path_by_category( - "training_job", f"{name}_{random_str(6)}" - ) - return f"oss://{bucket_name}/{job_output_path}" - - @classmethod - def _get_default_output_channel_defs(cls): - channel_defs = [ - { - "Name": DEFAULT_OUTPUT_MODEL_CHANNEL_NAME, - }, - { - "Name": DEFAULT_CHECKPOINT_CHANNEL_NAME, - }, - { - "Name": DEFAULT_TENSORBOARD_CHANNEL_NAME, - "Properties": { - "ossAppendable": "true", - }, - }, - ] - return channel_defs - - def _build_output_data_configs( - self, job_name: str, output_channel_defs: List[Dict[str, str]] - ) -> List[Dict[str, str]]: - """Build the output data config for the training job.""" - job_base_output_path = self._generate_job_base_output_path(job_name) - - # OSS URI for output channel will be mounted to directory - # "/ml/output/{ChannelName}/" and the output OSS URI should be a "directory" - def as_oss_dir_uri(uri: str): - return uri if uri.endswith("/") else uri + "/" - - res = [] - for ch in output_channel_defs: - # if checkpoint path is provided, use it as the checkpoint channel output. - if ch["Name"] == DEFAULT_CHECKPOINT_CHANNEL_NAME and self.checkpoints_path: - oss_uri = self.checkpoints_path - elif ( - ch["Name"] == DEFAULT_TENSORBOARD_CHANNEL_NAME - and self.experiment_config - ): - continue - elif not self.output_path and self.experiment_config: - continue - else: - oss_uri = as_oss_dir_uri( - posixpath.join(job_base_output_path, ch["Name"]) - ) - res.append( - { - "Name": ch["Name"], - "OutputUri": oss_uri, - } - ) - - return res - @abstractmethod def fit( self, inputs: Dict[str, Any] = None, wait: bool = True, show_logs: bool = True ): """Submit a training job with the given input data.""" - def wait(self, show_logs: bool = True): - """Block until the latest training job is completed. - - Args: - show_logs(bool): Specifies whether to fetch and print the logs produced by - the training job. - - Raises: - RuntimeError: If no training job is submitted. - - """ - if not self._latest_training_job: - raise RuntimeError("Could not find a submitted training job.") - self._latest_training_job.wait(show_logs=show_logs) - def model_data(self) -> str: """Model data output path. @@ -482,18 +323,18 @@ def model_data(self) -> str: str: A string in OSS URI format refers to the output model of the submitted job. """ - if not self._latest_training_job: + if not self.latest_job: raise RuntimeError( "No TrainingJob for the estimator, output model data not found." ) - if not self._latest_training_job.is_succeeded(): + if not self.latest_job.is_succeeded(): logger.warning( "The TrainingJob is currently not in a succeeded status, which means" " that the model data output may not be accessible." ) - return self._latest_training_job.output_path( + return self.latest_job.output_path( channel_name=DEFAULT_OUTPUT_MODEL_CHANNEL_NAME ) @@ -504,14 +345,12 @@ def checkpoints_data(self) -> str: str: A string in OSS URI format refers to the checkpoints of submitted training job. """ - if not self._latest_training_job: + if not self.latest_job: raise RuntimeError( "No TrainingJob for the Estimator, output checkpoints data path " "not found." ) - return self._latest_training_job.output_path( - channel_name=DEFAULT_CHECKPOINT_CHANNEL_NAME - ) + return self.latest_job.output_path(channel_name=DEFAULT_CHECKPOINT_CHANNEL_NAME) def tensorboard_data(self) -> str: """Output TensorBoard logs path. @@ -520,12 +359,12 @@ def tensorboard_data(self) -> str: str: A string in OSS URI format refers to the tensorboard log of submitted training job. """ - if not self._latest_training_job: + if not self.latest_job: raise RuntimeError( "No TrainingJob for the Estimator, output TensorBoard logs data path" " not found." ) - return self._latest_training_job.output_path( + return self.latest_job.output_path( channel_name=DEFAULT_TENSORBOARD_CHANNEL_NAME, ) @@ -540,15 +379,15 @@ def tensorboard(self, wait=True): """ from pai.tensorboard import TensorBoard - if not self.latest_training_job: + if not self.latest_job: raise RuntimeError("Could not find a submitted training job.") source_type = "TrainingJob" - if isinstance(self.latest_training_job, _LocalTrainingJob): + if isinstance(self.latest_job, LocalTrainingJob): raise RuntimeError("Local training job does not support tensorboard.") res = self.session.tensorboard_api.list( source_type=source_type, - source_id=self.latest_training_job.training_job_id, + source_id=self.latest_job.training_job_id, ) if res.items: @@ -564,8 +403,8 @@ def tensorboard(self, wait=True): tb = TensorBoard.create( uri=self.tensorboard_data(), wait=wait, - display_name=self._latest_training_job.training_job_name, - source_id=self.latest_training_job.training_job_id, + display_name=self.latest_job.training_job_name, + source_id=self.latest_job.training_job_id, source_type=source_type, session=self.session, ) @@ -871,8 +710,6 @@ def __init__( session=session, ) - self.__uploaded_source_files = None - def training_image_uri(self) -> str: """Return the Docker image to use for training. @@ -893,73 +730,35 @@ def _prepare_for_training(self): ) self.source_dir = updated_args["source_dir"] - def _upload_source_files(self, job_name: str) -> Optional[str]: - """Upload local source files to OSS.""" - if not self.source_dir: - return - - if is_oss_uri(self.source_dir): - return self.source_dir - elif not os.path.exists(self.source_dir): - raise ValueError(f"Source directory {self.source_dir} does not exist.") - # compress the source files to a Tar Gz file and upload to OSS bucket. - upload_data_path = self.session.get_storage_path_by_category( - "training_src", to_plain_text(job_name) - ) - self.__uploaded_source_files = upload( - source_path=self.source_dir, - oss_path=upload_data_path, - bucket=self.session.oss_bucket, - is_tar=True, - ) - return self.__uploaded_source_files - - def _build_code_input(self, job_name: str) -> Optional[Dict[str, Any]]: - """Build a dict to represent AlgorithmSpecCodeDir used in the TrainingJob.""" - upload_source_files = self._upload_source_files(job_name) - if not upload_source_files: - return - oss_uri_obj = OssUriObj( - uri=self.session.patch_oss_endpoint(upload_source_files) - ) - - code_dir = { - "LocationType": "oss", - "LocationValue": { - "Bucket": oss_uri_obj.bucket_name, - "Key": oss_uri_obj.object_key, - "Endpoint": oss_uri_obj.endpoint, - }, - } - return code_dir - def _build_algorithm_spec( - self, - code_input, - ) -> Dict[str, Any]: + self, code_input, inputs: Dict[str, Any] + ) -> AlgorithmSpec: """Build a temporary AlgorithmSpec used for submitting the TrainingJob.""" - command = ( - self.command - if isinstance(self.command, list) - else [ - "/bin/sh", - "-c", - self.command, - ] + algorithm_spec = AlgorithmSpec( + command=( + self.command + if isinstance(self.command, list) + else ["sh", "-c", self.command] + ), + image=self.training_image_uri(), + job_type=self.job_type, + metric_definitions=self.metric_definitions, + code_dir=code_input, + output_channels=self._default_training_output_channels(), + input_channels=[ + Channel(name=channel_name, required=False) + for channel_name in inputs.keys() + ], ) - algo_spec = { - "Command": command, - "Image": self.training_image_uri(), - "JobType": self.job_type, - "MetricDefinitions": self.metric_definitions, - "CodeDir": code_input, - "OutputChannels": self._get_default_output_channel_defs(), - } - return algo_spec + return algorithm_spec def fit( - self, inputs: Dict[str, Any] = None, wait: bool = True, show_logs: bool = True - ): + self, + inputs: Dict[str, Any] = None, + wait: bool = True, + show_logs: bool = True, + job_name: Optional[str] = None, + ) -> Union[TrainingJob, LocalTrainingJob]: """Submit a training job with the given input data. Args: @@ -972,78 +771,100 @@ def fit( either succeeded, failed, or stopped. (Default True). show_logs (bool): Specifies whether to show the logs produced by the training job (Default True). + job_name (str, optional): The name of the training job. + + Returns: + :class:`pai.job.TrainingJob` or :class:`pai.job.LocalTrainingJob`: A + submitted training job. + Raises: UnExpectedStatusException: If the training job fails. """ inputs = inputs or dict() self._prepare_for_training() - job_name = self._gen_job_display_name() + job_name = self.job_name(job_name=job_name) if is_local_run_instance_type(self.instance_type): - training_job = self._local_run( - job_name=job_name, inputs=inputs, instance_type=self.instance_type + return self._local_run( + job_name=job_name, + inputs=inputs, + instance_type=self.instance_type, + wait=wait, ) - else: - training_job = self._fit(inputs=inputs, job_name=job_name) - self._latest_training_job = training_job - - if wait: - self.wait(show_logs=show_logs) - - def _fit(self, job_name, inputs: Dict[str, Any] = None): - input_configs = self._build_input_data_configs(inputs) - output_configs = self._build_output_data_configs( - job_name, output_channel_defs=self._get_default_output_channel_defs() + return self._fit( + inputs=inputs, job_name=job_name, wait=wait, show_logs=show_logs ) + + def _fit( + self, + job_name, + inputs: Dict[str, Any], + wait: bool = True, + show_logs: bool = True, + ) -> TrainingJob: # prepare input code. - code_input = self._build_code_input(job_name) + code_input = self._build_code_input(job_name, source_dir=self.source_dir) algo_spec = self._build_algorithm_spec( code_input=code_input, + inputs=inputs, + ) + inputs = self.build_inputs( + inputs=inputs, + input_channels=algo_spec.input_channels, ) - training_job_id = self.session.training_job_api.create( - instance_count=self.instance_count, + if self.checkpoints_path: + outputs = {DEFAULT_CHECKPOINT_CHANNEL_NAME: self.checkpoints_path} + else: + outputs = None + + outputs = self.build_outputs( + job_name=job_name, + output_channels=algo_spec.output_channels, + outputs=outputs, + ) + + return self._submit( + job_name=job_name, + algorithm_spec=algo_spec, instance_spec=self.instance_spec, instance_type=self.instance_type, + instance_count=self.instance_count, resource_id=self.resource_id, - job_name=job_name, hyperparameters=self.hyperparameters, environments=self.environments, requirements=self.requirements, - max_running_in_seconds=self.max_run_time, - input_channels=input_configs, - output_channels=output_configs, - algorithm_spec=algo_spec, - user_vpc_config=self.user_vpc_config.to_dict() - if self.user_vpc_config - else None, - experiment_config=self.experiment_config.to_dict() - if self.experiment_config - else None, + max_run_time=self.max_run_time, + inputs=inputs, + outputs=outputs, + user_vpc_config=self.user_vpc_config if self.user_vpc_config else None, + experiment_config=( + self.experiment_config if self.experiment_config else None + ), labels=self.labels, + wait=wait, + show_logs=show_logs, ) - training_job = _TrainingJob.get(training_job_id) - print( - f"View the job detail by accessing the console URI: {training_job.console_uri}" - ) - return training_job def _local_run( self, job_name, instance_type: str, inputs: Dict[str, Any] = None, - ) -> "_LocalTrainingJob": + wait: bool = True, + ) -> "LocalTrainingJob": if self.instance_count > 1: raise RuntimeError("Local training job only supports single instance.") - training_job = _LocalTrainingJob( + training_job = LocalTrainingJob( estimator=self, inputs=inputs, job_name=job_name, instance_type=instance_type, ) training_job.run() + if wait: + training_job.wait() return training_job @@ -1086,7 +907,7 @@ def __init__( algorithm_name: Optional[str] = None, algorithm_version: Optional[str] = None, algorithm_provider: Optional[str] = None, - algorithm_spec: Optional[Dict[str, Any]] = None, + algorithm_spec: Optional[AlgorithmSpec] = None, hyperparameters: Optional[Dict[str, Any]] = None, environments: Optional[Dict[str, str]] = None, requirements: Optional[List[str]] = None, @@ -1113,7 +934,7 @@ def __init__( a PAI official algorithm. If not provided, the default provider is user's PAI account. If algorithm name is not provided, this argument will be ignored. - algorithm_spec (Dict[str, Any], optional): A temporary algorithm spec. + algorithm_spec (AlgorithmSpec, optional): A temporary algorithm spec. Required if algorithm_name is not provided. hyperparameters (dict, optional): A dictionary that represents the hyperparameters used in the training job. Default hyperparameters will @@ -1169,7 +990,9 @@ def __init__( algorithm_version=algorithm_version, algorithm_provider=algorithm_provider, ) - self._algo_spec = _algo_version["AlgorithmSpec"] + self._algo_spec = AlgorithmSpec.model_validate( + _algo_version["AlgorithmSpec"] + ) self.algorithm_name = _algo_version["AlgorithmName"] self.algorithm_version = _algo_version["AlgorithmVersion"] self.algorithm_provider = _algo_version["AlgorithmProvider"] @@ -1205,37 +1028,32 @@ def set_hyperparameters(self, **kwargs): super(AlgorithmEstimator, self).set_hyperparameters(**kwargs) @property - def hyperparameter_definitions(self) -> List[Dict[str, Any]]: + def hyperparameter_definitions(self) -> List[HyperParameterDefinition]: """Get the hyperparameter definitions from the algorithm spec.""" - res = self._algo_spec.get("HyperParameters", []) + res = self._algo_spec.hyperparameter_definitions return res @property - def input_channel_definitions(self) -> List[Dict[str, Any]]: + def input_channel_definitions(self) -> List[Channel]: """Get the input channel definitions from the algorithm spec.""" - res = self._algo_spec.get("InputChannels", []) + res = self._algo_spec.input_channels return res @property - def output_channel_definitions(self) -> List[Dict[str, Any]]: + def output_channel_definitions(self) -> List[Channel]: """Get the output channel definitions from the algorithm spec.""" - res = self._algo_spec.get("OutputChannels", []) + res = self._algo_spec.output_channels return res @property def supported_instance_types(self) -> List[str]: """Get the supported instance types from the algorithm spec.""" - res = ( - self._algo_spec["SupportedInstanceTypes"] - if "SupportedInstanceTypes" in self._algo_spec - else [] - ) - return res + return self._algo_spec.supported_instance_types def _check_args( self, algorithm_name: str, - algorithm_spec: Dict[str, Any], + algorithm_spec: Optional[AlgorithmSpec], ): """Check the algorithm_name and algorithm_spec. @@ -1245,7 +1063,7 @@ def _check_args( Args: algorithm_name (str): The name of the algorithm. - algorithm_spec (dict): The algorithm spec. + algorithm_spec (AlgorithmSpec): The algorithm spec. """ if not algorithm_name and not algorithm_spec: raise ValueError( @@ -1382,8 +1200,12 @@ def _get_default_training_instance_type(self) -> str: return machine_spec["InstanceType"] def fit( - self, inputs: Dict[str, Any] = None, wait: bool = True, show_logs: bool = True - ): + self, + inputs: Dict[str, Any] = None, + wait: bool = True, + show_logs: bool = True, + job_name: Optional[str] = None, + ) -> TrainingJob: """Submit a training job with the given input data. Args: @@ -1396,56 +1218,50 @@ def fit( either succeeded, failed, or stopped. (Default True). show_logs (bool): Specifies whether to show the logs produced by the training job (Default True). + job_name (str, optional): The name of the training job. + + Returns: + :class:`pai.training_job.TrainingJob`: The submitted training job. + Raises: UnExpectedStatusException: If the training job fails. """ - inputs = inputs or dict() - job_name = self._gen_job_display_name() - training_job = self._fit(inputs=inputs, job_name=job_name) - self._latest_training_job = training_job - - if wait: - self.wait(show_logs=show_logs) - - def _fit(self, job_name, inputs: Dict[str, Any] = None): - input_configs = self._build_input_data_configs( - inputs, input_channel_defs=self.input_channel_definitions + job_name = self.job_name(job_name=job_name) + input_configs = self.build_inputs( + inputs, + input_channels=self._algo_spec.input_channels, ) - output_configs = self._build_output_data_configs( - job_name, output_channel_defs=self.output_channel_definitions + output_configs = self.build_outputs( + job_name, + output_channels=self._algo_spec.output_channels, ) - - training_job_id = self.session.training_job_api.create( + return self._submit( instance_count=self.instance_count, instance_type=self.instance_type, instance_spec=self.instance_spec, resource_id=self.resource_id, job_name=job_name, hyperparameters=self.hyperparameters, - max_running_in_seconds=self.max_run_time, - input_channels=input_configs, - output_channels=output_configs, + max_run_time=self.max_run_time, + inputs=input_configs, + outputs=output_configs, environments=self.environments, requirements=self.requirements, algorithm_name=self.algorithm_name, algorithm_version=self.algorithm_version, algorithm_provider=self.algorithm_provider, algorithm_spec=self.algorithm_spec, - user_vpc_config=self.user_vpc_config.to_dict() - if self.user_vpc_config - else None, - experiment_config=self.experiment_config.to_dict() - if self.experiment_config - else None, + user_vpc_config=( + self.user_vpc_config.to_dict() if self.user_vpc_config else None + ), + experiment_config=( + self.experiment_config.to_dict() if self.experiment_config else None + ), labels=self.labels, + wait=wait, + show_logs=show_logs, ) - training_job = _TrainingJob.get(training_job_id) - print( - f"View the job detail by accessing the console URI:" - f" {training_job.console_uri}" - ) - return training_job def get_outputs_data(self) -> Dict[str, str]: """Show all outputs data paths. @@ -1453,543 +1269,27 @@ def get_outputs_data(self) -> Dict[str, str]: Returns: dict[str, str]: A dictionary of all outputs data paths. """ - if not self._latest_training_job: + if not self.latest_job: raise RuntimeError( - "No TrainingJob for the estimator, output checkpoints data not found." - ) - - return { - ch["Name"]: ch["OutputUri"] - for ch in self._latest_training_job.output_channels - } - - -_TRAINING_LAUNCH_SCRIPT_TEMPLATE = textwrap.dedent( - """\ -#!/bin/sh - -env - -# change to working directory -if [ -n "$PAI_WORKING_DIR" ]; then - echo "Change to Working Directory", $PAI_WORKING_DIR - mkdir -p $PAI_WORKING_DIR && cd $PAI_WORKING_DIR -fi - -# install requirements -if [ -e "requirements.txt" ]; then - echo "Installing dependencies from requirements.txt" - python -m pip install -r requirements.txt -fi - -echo "User program launching" -echo "-----------------------------------------------------------------" - -sh {0} -""" -) - - -class _TrainingEnv(object): - ENV_PAI_HPS = "PAI_HPS" - ENV_PAI_HPS_PREFIX = "PAI_HPS_" - ENV_PAI_USER_ARGS = "PAI_USER_ARGS" - ENV_PAI_INPUT_PREFIX = "PAI_INPUT_" - ENV_PAI_OUTPUT_PREFIX = "PAI_OUTPUT_" - ENV_PAI_WORKING_DIR = "PAI_WORKING_DIR" - - -class _TrainingJobConfig(object): - WORKING_DIR = "/ml/usercode/" - INPUT_CONFIG_DIR = "/ml/input/config/" - INPUT_DATA_DIR = "/ml/input/data/" - OUTPUT_DIR = "/ml/output/" - - -_ENV_NOT_ALLOWED_CHARS = re.compile(r"[^a-zA-Z0-9_]") - - -class _LocalTrainingJob(object): - """A class that represents a local training job running with docker container.""" - - def __init__( - self, - estimator: Estimator, - inputs: Dict[str, Any], - instance_type: str = None, - temp_dir: str = None, - job_name: str = None, - ): - self.estimator = estimator - self.inputs = inputs - self.tmp_dir = temp_dir or tempfile.mkdtemp() - self.job_name = job_name - self.instance_type = instance_type - logger.info("Local TrainingJob temporary directory: {}".format(self.tmp_dir)) - self._container_run: ContainerRun = None - - def __str__(self): - return self.__repr__() - - def __repr__(self): - if self._container_run: - container = self._container_run.container - container_name, container_id, status = ( - container.name, - container.id, - container.status, + "Could not find a submitted training job. Please submit a training job" + " before calling this method." ) - else: - container_name, container_id, status = None, None, None - return f"LocalTrainingJob(container_name={container_name}, container_id={container_id}, status={status})" - - @property - def session(self) -> Session: - return self.estimator.session - - def prepare_env(self) -> Dict[str, str]: - """Prepare environment variables for the training job.""" - - # Hyperparameters environment variables - def _normalize_name(name: str) -> str: - # replace all non-alphanumeric characters with underscore - return _ENV_NOT_ALLOWED_CHARS.sub("_", name).upper() - - env = {} - user_args = [] - for name, value in self.estimator.hyperparameters.items(): - env[_TrainingEnv.ENV_PAI_HPS_PREFIX + _normalize_name(name)] = str(value) - user_args.extend(["--" + name, shlex.quote(str(value))]) - env[_TrainingEnv.ENV_PAI_USER_ARGS] = " ".join( - [shlex.quote(v) for v in user_args] - ) - env[_TrainingEnv.ENV_PAI_HPS] = json.dumps( - {name: str(value) for name, value in self.estimator.hyperparameters.items()} - ) - # Environments for input channel - for name, value in self.inputs.items(): - if (is_oss_uri(value) and value.endswith("/")) or os.path.isdir(value): - env[ - _TrainingEnv.ENV_PAI_INPUT_PREFIX + _normalize_name(name) - ] = posixpath.join(_TrainingJobConfig.INPUT_DATA_DIR, name) - else: - file_name = os.path.basename(value) - env[ - _TrainingEnv.ENV_PAI_INPUT_PREFIX + _normalize_name(name) - ] = posixpath.join(_TrainingJobConfig.INPUT_DATA_DIR, name, file_name) - - # Environments for output channel. - # By default, TrainingJob invoked by Estimator will have two output channels: - # 'model' and 'checkpoints' - output_channel = ["model", "checkpoints"] - for name in output_channel: - env[ - _TrainingEnv.ENV_PAI_OUTPUT_PREFIX + _normalize_name(name) - ] = posixpath.join(_TrainingJobConfig.OUTPUT_DIR, name) - - env[_TrainingEnv.ENV_PAI_WORKING_DIR] = _TrainingJobConfig.WORKING_DIR - return env - - def run(self): - """Run estimator job in local with docker.""" - output_model_path = self.output_path() - os.makedirs(output_model_path, exist_ok=True) - volumes = {} - - tmp_dir = tempfile.mkdtemp() - # 1. Prepare source code to directory /ml/usercode - user_code_dir = os.path.join(self.tmp_dir, "user_code") - if is_oss_uri(self.estimator.source_dir): - raise RuntimeError("OSS source code is not supported in local training.") - shutil.copytree(self.estimator.source_dir, user_code_dir) - volumes[user_code_dir] = { - "bind": _TrainingJobConfig.WORKING_DIR, - "mode": "rw", - } - - # 2. Prepare input data for training job. - input_data = self.prepare_input_data() - for host_path, container_path in input_data.items(): - volumes[host_path] = { - "bind": container_path, - "mode": "rw", - } - - # 3. Prepare input config files, such as hyperparameters.json, - # training-job.json, etc. - input_config_path = os.path.join(tmp_dir, "config") - os.makedirs(input_config_path, exist_ok=True) - self.prepare_input_config(input_config_path=input_config_path) - volumes[input_config_path] = { - "bind": _TrainingJobConfig.INPUT_CONFIG_DIR, - "mode": "rw", - } - - execution_dir = os.path.join(tmp_dir, "config", "execution") - os.makedirs(execution_dir, exist_ok=True) - command_path = os.path.join(execution_dir, "command.sh") - with open(command_path, "w") as f: - f.write(self.estimator.command) - launch_script_path = os.path.join(input_config_path, "launch.sh") - with open(launch_script_path, "w") as f: - f.write( - _TRAINING_LAUNCH_SCRIPT_TEMPLATE.format( - posixpath.join( - _TrainingJobConfig.INPUT_CONFIG_DIR, "execution/command.sh" - ) - ) - ) - - # 4. Config output model channel - volumes[output_model_path] = { - "bind": posixpath.join(_TrainingJobConfig.OUTPUT_DIR, "model"), - "mode": "rw", - } - - gpu_count = ( - -1 if self.instance_type.strip() == INSTANCE_TYPE_LOCAL_GPU else None - ) - self._container_run = run_container( - environment_variables=self.prepare_env(), - image_uri=self.estimator.image_uri, - entry_point=[ - "/bin/sh", - posixpath.join(_TrainingJobConfig.INPUT_CONFIG_DIR, "launch.sh"), - ], - volumes=volumes, - working_dir=_TrainingJobConfig.WORKING_DIR, - gpu_count=gpu_count, - ) - - def prepare_input_config(self, input_config_path): - """Prepare input config for TrainingJob, such as hyperparameters.json, - trainingjob.json.""" - with open(os.path.join(input_config_path, "hyperparameters.json"), "w") as f: - hps = self.estimator.hyperparameters or dict() - f.write(json.dumps({k: str(v) for k, v in hps.items()})) - - def prepare_input_data(self) -> Dict[str, str]: - """Prepare input data config.""" - input_data_configs = {} - - for name, input_data in self.inputs.items(): - local_channel_path = os.path.join(self.tmp_dir, f"input/data/{name}") - os.makedirs(local_channel_path, exist_ok=True) - input_data_configs[local_channel_path] = posixpath.join( - _TrainingJobConfig.INPUT_DATA_DIR, name - ) - if is_oss_uri(input_data): - oss_uri_obj = OssUriObj(input_data) - oss_bucket = self.session.get_oss_bucket(oss_uri_obj.bucket_name) - os.makedirs(local_channel_path, exist_ok=True) - download( - oss_uri_obj.object_key, - local_path=local_channel_path, - bucket=oss_bucket, - ) - input_data_configs[local_channel_path] = posixpath.join( - _TrainingJobConfig.INPUT_DATA_DIR, name - ) - else: - # If the input data is local files, copy the input data to a - # temporary directory. - if not os.path.exists(input_data): - raise ValueError( - "Input data not exists: name={} input_data={}".format( - name, input_data - ) - ) - elif os.path.isdir(input_data): - distutils.dir_util.copy_tree(input_data, local_channel_path) - else: - shutil.copy( - input_data, - os.path.join(local_channel_path, os.path.basename(input_data)), - ) - - return input_data_configs - - def wait(self, show_logs: bool = True): - self._container_run.watch(show_logs=show_logs) - - def output_path(self, channel_name="model"): - return os.path.join(self.tmp_dir, "output", f"{channel_name}/") - - def is_succeeded(self): - """Return True if the training job is succeeded, otherwise return False.""" - return self._container_run.is_succeeded() - - -class TrainingJobStatus(object): - CreateFailed = "CreateFailed" - InitializeFailed = "InitializeFailed" - Succeed = "Succeed" - Failed = "Failed" - Terminated = "Terminated" - Creating = "Creating" - Created = "Created" - Initializing = "Initializing" - Submitted = "Submitted" - Running = "Running" - - @classmethod - def completed_status(cls): - return [ - cls.InitializeFailed, - cls.Succeed, - cls.Failed, - cls.Terminated, + uri_outputs = [ + output + for output in self.latest_job.outputs + if isinstance(output, UriOutput) ] - - @classmethod - def failed_status(cls): - return [ - cls.InitializeFailed, - cls.Failed, - cls.CreateFailed, + extra_outputs = [ + output + for output in self.latest_job.outputs + if not isinstance(output, UriOutput) ] - -class TrainingJobChannel(object): - def __init__(self, dataset_id=None, input_uri=None, name=None): - self.dataset_id = dataset_id - self.input_uri = input_uri - self.name = name - - -class _TrainingJob(EntityBaseMixin): - _schema_cls = TrainingJobSchema - - def __init__( - self, - algorithm_name=None, - algorithm_version="1.0.0", - algorithm_provider=ProviderAlibabaPAI, - hyperparameters: Dict[str, Any] = None, - training_job_name: str = None, - instance_type: str = None, - instance_count: int = None, - output_channels: List[Dict[str, str]] = None, - input_channels: List[Dict[str, str]] = None, - labels: Dict[str, str] = None, - max_running_time_in_seconds: int = None, - experiment_config: Dict[str, str] = None, - description: str = None, - session: Session = None, - **kwargs, - ): - super(_TrainingJob, self).__init__(session=session, **kwargs) - session = session or get_default_session() - self.algorithm_name = algorithm_name - self.algorithm_version = algorithm_version - self.algorithm_provider = algorithm_provider - self.training_job_name = training_job_name - self.description = description - self.labels = labels - self.hyperparameters = hyperparameters - self.input_channels = input_channels - self.output_channels = output_channels - self.instance_type = instance_type - self.instance_count = instance_count - self.max_running_time_in_seconds = max_running_time_in_seconds - self.experiment_config = experiment_config - - # Load only fields - self.create_time = kwargs.pop("create_time", None) - self.modified_time = kwargs.pop("modified_time", None) - self.reason_code = kwargs.pop("reason_code", None) - self.reason_message = kwargs.pop("reason_message", None) - self.status = kwargs.pop("status", None) - self.status_transitions = kwargs.pop("status_transitions", None) - self.training_job_id = kwargs.pop("training_job_id", None) - self.training_job_url = kwargs.pop("training_job_url", None) - - def __repr__(self): - return "TrainingJob(id={})".format(self.training_job_id) - - def __str__(self): - return self.__repr__() - - @property - def id(self): - return self.training_job_id - - @classmethod - def get(cls, training_job_id, session: Session = None) -> "_TrainingJob": - session = session or get_default_session() - res = session.training_job_api.get(training_job_id=training_job_id) - return cls.from_api_object(res, session=session) - - @classmethod - def list( - cls, - status=None, - session: Session = None, - page_size=50, - page_number=1, - ): - session = session or get_default_session() - res = session.training_job_api.list( - status=status, page_size=page_size, page_number=page_number - ) - return [cls.from_api_object(item, session=session) for item in res.items] - - def output_path(self, channel_name="model"): - for output_channel in self.output_channels: - if output_channel["Name"] == channel_name: - return output_channel["OutputUri"] - raise RuntimeError( - f"Output channel is not specified: channel_name={channel_name}" - ) - - @property - def console_uri(self): - if not self.training_job_id: - raise ValueError("The TrainingJob is not submitted") - - return self.training_job_url - - def wait(self, interval=2, show_logs: bool = True): - self.session.training_job_api.refresh_entity(self.training_job_id, self) - - if show_logs: - job_log_printer = _TrainingJobLogPrinter( - training_job_id=self.training_job_id, page_size=20, session=self.session - ) - job_log_printer.start() - else: - job_log_printer = None - try: - while not self.is_completed(): - time.sleep(interval) - finally: - if job_log_printer: - job_log_printer.stop(wait=True) - - self._on_job_completed() - - def _on_job_completed(self): - # Print an empty line to separate the training job logs and the following logs - print() - if self.status == TrainingJobStatus.Succeed: - print( - f"Training job ({self.training_job_id}) succeeded, you can check the" - f" logs/metrics/output in the console:\n{self.console_uri}" - ) - elif self.status == TrainingJobStatus.Terminated: - print( - f"Training job is ended with status {self.status}: " - f"reason_code={self.reason_code}, reason_message={self.reason_message}." - f"Check the training job in the console:\n{self.console_uri}" - ) - elif self.status in TrainingJobStatus.failed_status(): - print( - f"Training job ({self.training_job_id}) failed, please check the logs" - f" in the console: \n{self.console_uri}" - ) - - message = f"TrainingJob failed: name={self.training_job_name}, " - f"training_job_id={self.training_job_id}, " - f"reason_code={self.reason_code}, status={self.status}, " - f"reason_message={self.reason_message}" - - raise UnexpectedStatusException(message=message, status=self.status) - - def _reload(self): - """Reload the training job from the PAI Service,""" - self.session.training_job_api.refresh_entity(self.training_job_id, self) - - def is_succeeded(self): - """Return True if the training job is succeeded""" - self._reload() - return self.status == TrainingJobStatus.Succeed - - @retry(wait_secs=10) - def is_completed(self): - """Return True if the training job is completed, including failed status""" - if self.status in TrainingJobStatus.completed_status(): - return True - self._reload() - - return self.status in TrainingJobStatus.completed_status() - - -class _TrainingJobLogPrinter(object): - """A class used to print logs for a training job""" - - executor = ThreadPoolExecutor(5) - - def __init__( - self, training_job_id: str, page_size=10, session: Optional[Session] = None - ): - self.training_job_id = training_job_id - self.session = session - self.page_size = page_size - self._future = None - self._stop = False - - def _list_logs_api(self, page_number: int = 1): - try: - res = self.session.training_job_api.list_logs( - self.training_job_id, - page_number=page_number, - page_size=self.page_size, + if extra_outputs: + logger.warning( + "Extra outputs are provided in the training job, but only URI outputs" + " are supported. The extra outputs will be ignored: %s", + extra_outputs, ) - return res - except TeaException as e: - # hack: Backend service may raise an exception when the training job - # instance is not found. - if e.code == "TRAINING_JOB_INSTANCE_NOT_FOUND": - return PaginatedResult(items=[], total_count=0) - else: - raise e - - def _list_logs(self): - page_number, page_offset = 1, 0 - # print training job logs. - while not self._stop: - res = self._list_logs_api(page_number=page_number) - # 1. move to next page - if len(res.items) == self.page_size: - # print new logs starting from page_offset - self._print_logs(logs=res.items[page_offset:]) - page_number += 1 - page_offset = 0 - # 2. stay at the current page. - else: - if len(res.items) > page_offset: - # print new logs starting from page_offset - self._print_logs(logs=res.items[page_offset:]) - page_offset = len(res.items) - time.sleep(1) - - # When _stop is True, wait and print remaining logs. - time.sleep(10) - while True: - res = self._list_logs_api(page_number=page_number) - # There maybe more logs in the next page - if len(res.items) == self.page_size: - self._print_logs(logs=res.items[page_offset:]) - page_number += 1 - page_offset = 0 - # No more logs in the next page. - else: - if len(res.items) > page_offset: - self._print_logs(logs=res.items[page_offset:]) - break - - def _print_logs(self, logs: List[str]): - for log in logs: - print(log) - - def start(self): - if self._future: - raise ValueError("The training job log printer is already started") - self._stop = False - self._future = self.executor.submit(self._list_logs) - - def stop(self, wait: bool = True): - self._stop = True - if self._future: - self._future.result() + return {ch.name: ch.output_uri for ch in uri_outputs} diff --git a/pai/experiment.py b/pai/experiment.py index 1087342..7f1f1c0 100644 --- a/pai/experiment.py +++ b/pai/experiment.py @@ -21,28 +21,6 @@ logger = get_logger(__name__) -_default_session = None - - -class ExperimentConfig(object): - """ExperimentConfig is used to configure the experiment to which the job belongs.""" - - def __init__( - self, - experiment_id: str, - ): - """Initialize ExperimentConfig. - Args: - experiment_id (str): Specifies the ID of the experiment that training job instance - belongs to. - """ - self.experiment_id = experiment_id - - def to_dict(self): - return { - "ExperimentId": self.experiment_id, - } - class Experiment(object): """An experiment is a collection of runs. It can be used to compare the @@ -146,6 +124,26 @@ def get(cls, experiment_id: str) -> "Experiment": session=session, ) + @classmethod + def get_by_name( + cls, name: str, session: Optional[Session] = None + ) -> Optional["Experiment"]: + """Get experiment by name. + + Args: + name (str): The name of the experiment. + session (Session): The session to be used. + + Returns: + Experiment: The experiment with the specified name. + + """ + exp = next( + (exp for exp in cls.list(name=name, session=session) if exp.name == name), + None, + ) + return exp + def update( self, name: str, diff --git a/pai/job/__init__.py b/pai/job/__init__.py new file mode 100644 index 0000000..85ae444 --- /dev/null +++ b/pai/job/__init__.py @@ -0,0 +1,48 @@ +# Copyright 2024 Alibaba, Inc. or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ._local_training_job import LocalTrainingJob +from ._training_job import ( + AlgorithmSpec, + Channel, + CodeDir, + ExperimentConfig, + HyperParameterDefinition, + InstanceSpec, + ModelTrainingSpec, + OssLocation, + TrainingJob, + TrainingJobStatus, + UriInput, + UriOutput, + UserVpcConfig, + _TrainingJobSubmitter, +) + +__all__ = [ + "TrainingJob", + "ModelTrainingSpec", + "TrainingJobStatus", + "Channel", + "HyperParameterDefinition", + "OssLocation", + "AlgorithmSpec", + "CodeDir", + "LocalTrainingJob", + "UriOutput", + "UserVpcConfig", + "ExperimentConfig", + "InstanceSpec", + "UriInput", +] diff --git a/pai/job/_local_training_job.py b/pai/job/_local_training_job.py new file mode 100644 index 0000000..5f9c1d7 --- /dev/null +++ b/pai/job/_local_training_job.py @@ -0,0 +1,293 @@ +# Copyright 2024 Alibaba, Inc. or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import distutils.dir_util +import json +import os +import posixpath +import re +import shlex +import shutil +import tempfile +import textwrap +import typing +from typing import Any, Dict + +from pai.session import Session + +from ..common.consts import INSTANCE_TYPE_LOCAL_GPU +from ..common.docker_utils import ContainerRun, run_container +from ..common.logging import get_logger +from ..common.oss_utils import OssUriObj, download, is_oss_uri + +if typing.TYPE_CHECKING: + from ..estimator import Estimator + + +logger = get_logger(__name__) + + +class _TrainingEnv(object): + ENV_PAI_HPS = "PAI_HPS" + ENV_PAI_HPS_PREFIX = "PAI_HPS_" + ENV_PAI_USER_ARGS = "PAI_USER_ARGS" + ENV_PAI_INPUT_PREFIX = "PAI_INPUT_" + ENV_PAI_OUTPUT_PREFIX = "PAI_OUTPUT_" + ENV_PAI_WORKING_DIR = "PAI_WORKING_DIR" + + +class _TrainingJobConfig(object): + WORKING_DIR = "/ml/usercode/" + INPUT_CONFIG_DIR = "/ml/input/config/" + INPUT_DATA_DIR = "/ml/input/data/" + OUTPUT_DIR = "/ml/output/" + + +_ENV_NOT_ALLOWED_CHARS = re.compile(r"[^a-zA-Z0-9_]") +_TRAINING_LAUNCH_SCRIPT_TEMPLATE = textwrap.dedent( + """\ +#!/bin/sh + +env + +# change to working directory +if [ -n "$PAI_WORKING_DIR" ]; then + echo "Change to Working Directory", $PAI_WORKING_DIR + mkdir -p $PAI_WORKING_DIR && cd $PAI_WORKING_DIR +fi + +# install requirements +if [ -e "requirements.txt" ]; then + echo "Installing dependencies from requirements.txt" + python -m pip install -r requirements.txt +fi + +echo "User program launching" +echo "-----------------------------------------------------------------" + +sh {0} +""" +) + + +class LocalTrainingJob(object): + """A class that represents a local training job running with docker container.""" + + def __init__( + self, + estimator: "Estimator", + inputs: Dict[str, Any], + instance_type: str = None, + temp_dir: str = None, + job_name: str = None, + ): + self.estimator = estimator + self.inputs = inputs + self.tmp_dir = temp_dir or tempfile.mkdtemp() + self.job_name = job_name + self.instance_type = instance_type + logger.info("Local TrainingJob temporary directory: {}".format(self.tmp_dir)) + self._container_run: ContainerRun = None + + def __str__(self): + return self.__repr__() + + def __repr__(self): + if self._container_run: + container = self._container_run.container + container_name, container_id, status = ( + container.name, + container.id, + container.status, + ) + else: + container_name, container_id, status = None, None, None + return f"LocalTrainingJob(container_name={container_name}, container_id={container_id}, status={status})" + + @property + def session(self) -> Session: + return self.estimator.session + + def prepare_env(self) -> Dict[str, str]: + """Prepare environment variables for the training job.""" + + # Hyperparameters environment variables + def _normalize_name(name: str) -> str: + # replace all non-alphanumeric characters with underscore + return _ENV_NOT_ALLOWED_CHARS.sub("_", name).upper() + + env = {} + user_args = [] + for name, value in self.estimator.hyperparameters.items(): + env[_TrainingEnv.ENV_PAI_HPS_PREFIX + _normalize_name(name)] = str(value) + user_args.extend(["--" + name, shlex.quote(str(value))]) + env[_TrainingEnv.ENV_PAI_USER_ARGS] = " ".join( + [shlex.quote(v) for v in user_args] + ) + env[_TrainingEnv.ENV_PAI_HPS] = json.dumps( + {name: str(value) for name, value in self.estimator.hyperparameters.items()} + ) + + # Environments for input channel + for name, value in self.inputs.items(): + if (is_oss_uri(value) and value.endswith("/")) or os.path.isdir(value): + env[ + _TrainingEnv.ENV_PAI_INPUT_PREFIX + _normalize_name(name) + ] = posixpath.join(_TrainingJobConfig.INPUT_DATA_DIR, name) + else: + file_name = os.path.basename(value) + env[ + _TrainingEnv.ENV_PAI_INPUT_PREFIX + _normalize_name(name) + ] = posixpath.join(_TrainingJobConfig.INPUT_DATA_DIR, name, file_name) + + # Environments for output channel. + # By default, TrainingJob invoked by Estimator will have two output channels: + # 'model' and 'checkpoints' + output_channel = ["model", "checkpoints"] + for name in output_channel: + env[ + _TrainingEnv.ENV_PAI_OUTPUT_PREFIX + _normalize_name(name) + ] = posixpath.join(_TrainingJobConfig.OUTPUT_DIR, name) + + env[_TrainingEnv.ENV_PAI_WORKING_DIR] = _TrainingJobConfig.WORKING_DIR + return env + + def run(self): + """Run estimator job in local with docker.""" + output_model_path = self.output_path() + os.makedirs(output_model_path, exist_ok=True) + volumes = {} + + tmp_dir = tempfile.mkdtemp() + # 1. Prepare source code to directory /ml/usercode + user_code_dir = os.path.join(self.tmp_dir, "user_code") + if is_oss_uri(self.estimator.source_dir): + raise RuntimeError("OSS source code is not supported in local training.") + shutil.copytree(self.estimator.source_dir, user_code_dir) + volumes[user_code_dir] = { + "bind": _TrainingJobConfig.WORKING_DIR, + "mode": "rw", + } + + # 2. Prepare input data for training job. + input_data = self.prepare_input_data() + for host_path, container_path in input_data.items(): + volumes[host_path] = { + "bind": container_path, + "mode": "rw", + } + + # 3. Prepare input config files, such as hyperparameters.json, + # training-job.json, etc. + input_config_path = os.path.join(tmp_dir, "config") + os.makedirs(input_config_path, exist_ok=True) + self.prepare_input_config(input_config_path=input_config_path) + volumes[input_config_path] = { + "bind": _TrainingJobConfig.INPUT_CONFIG_DIR, + "mode": "rw", + } + + execution_dir = os.path.join(tmp_dir, "config", "execution") + os.makedirs(execution_dir, exist_ok=True) + command_path = os.path.join(execution_dir, "command.sh") + with open(command_path, "w") as f: + f.write(self.estimator.command) + launch_script_path = os.path.join(input_config_path, "launch.sh") + with open(launch_script_path, "w") as f: + f.write( + _TRAINING_LAUNCH_SCRIPT_TEMPLATE.format( + posixpath.join( + _TrainingJobConfig.INPUT_CONFIG_DIR, "execution/command.sh" + ) + ) + ) + + # 4. Config output model channel + volumes[output_model_path] = { + "bind": posixpath.join(_TrainingJobConfig.OUTPUT_DIR, "model"), + "mode": "rw", + } + + gpu_count = ( + -1 if self.instance_type.strip() == INSTANCE_TYPE_LOCAL_GPU else None + ) + self._container_run = run_container( + environment_variables=self.prepare_env(), + image_uri=self.estimator.image_uri, + entry_point=[ + "/bin/sh", + posixpath.join(_TrainingJobConfig.INPUT_CONFIG_DIR, "launch.sh"), + ], + volumes=volumes, + working_dir=_TrainingJobConfig.WORKING_DIR, + gpu_count=gpu_count, + ) + + def prepare_input_config(self, input_config_path): + """Prepare input config for TrainingJob, such as hyperparameters.json, + trainingjob.json.""" + with open(os.path.join(input_config_path, "hyperparameters.json"), "w") as f: + hps = self.estimator.hyperparameters or dict() + f.write(json.dumps({k: str(v) for k, v in hps.items()})) + + def prepare_input_data(self) -> Dict[str, str]: + """Prepare input data config.""" + input_data_configs = {} + + for name, input_data in self.inputs.items(): + local_channel_path = os.path.join(self.tmp_dir, f"input/data/{name}") + os.makedirs(local_channel_path, exist_ok=True) + input_data_configs[local_channel_path] = posixpath.join( + _TrainingJobConfig.INPUT_DATA_DIR, name + ) + if is_oss_uri(input_data): + oss_uri_obj = OssUriObj(input_data) + oss_bucket = self.session.get_oss_bucket(oss_uri_obj.bucket_name) + os.makedirs(local_channel_path, exist_ok=True) + download( + oss_uri_obj.object_key, + local_path=local_channel_path, + bucket=oss_bucket, + ) + input_data_configs[local_channel_path] = posixpath.join( + _TrainingJobConfig.INPUT_DATA_DIR, name + ) + else: + # If the input data is local files, copy the input data to a + # temporary directory. + if not os.path.exists(input_data): + raise ValueError( + "Input data not exists: name={} input_data={}".format( + name, input_data + ) + ) + elif os.path.isdir(input_data): + distutils.dir_util.copy_tree(input_data, local_channel_path) + else: + shutil.copy( + input_data, + os.path.join(local_channel_path, os.path.basename(input_data)), + ) + + return input_data_configs + + def wait(self, show_logs: bool = True): + self._container_run.watch(show_logs=show_logs) + + def output_path(self, channel_name="model"): + return os.path.join(self.tmp_dir, "output", f"{channel_name}/") + + def is_succeeded(self): + """Return True if the training job is succeeded, otherwise return False.""" + return self._container_run.is_succeeded() diff --git a/pai/job/_training_job.py b/pai/job/_training_job.py new file mode 100644 index 0000000..ea2508e --- /dev/null +++ b/pai/job/_training_job.py @@ -0,0 +1,872 @@ +# Copyright 2024 Alibaba, Inc. or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import posixpath +import time +import typing +from concurrent.futures import ThreadPoolExecutor +from typing import Any, Dict, List, Optional, Union + +from pydantic import BaseModel, ConfigDict, Field +from pydantic.alias_generators import to_pascal +from Tea.exceptions import TeaException + +from ..api.base import PaginatedResult +from ..common.consts import StoragePathCategory +from ..common.logging import get_logger +from ..common.oss_utils import OssUriObj, is_oss_uri, upload +from ..common.utils import ( + is_dataset_id, + is_filesystem_uri, + is_odps_table_uri, + name_from_base, + print_table, + random_str, + retry, + to_plain_text, +) +from ..exception import UnexpectedStatusException +from ..session import Session, get_default_session + +if typing.TYPE_CHECKING: + from ..estimator import FileSystemInputBase + +logger = get_logger(__name__) + + +def as_oss_dir_uri(uri: str): + return uri if uri.endswith("/") else uri + "/" + + +DEFAULT_OUTPUT_MODEL_CHANNEL_NAME = "model" +DEFAULT_CHECKPOINT_CHANNEL_NAME = "checkpoints" +DEFAULT_TENSORBOARD_CHANNEL_NAME = "tensorboard" + + +class BaseAPIModel(BaseModel): + + model_config = ConfigDict( + alias_generator=to_pascal, + populate_by_name=True, + ) + + def model_dump(self, **kwargs) -> Dict[str, Any]: + kwargs.update({"by_alias": True, "exclude_none": True}) + return super().model_dump(**kwargs) + + def to_dict(self): + return self.model_dump() + + +class TrainingJobStatus(object): + CreateFailed = "CreateFailed" + InitializeFailed = "InitializeFailed" + Succeed = "Succeed" + Failed = "Failed" + Terminated = "Terminated" + Creating = "Creating" + Created = "Created" + Initializing = "Initializing" + Submitted = "Submitted" + Running = "Running" + + @classmethod + def completed_status(cls): + return [ + cls.InitializeFailed, + cls.Succeed, + cls.Failed, + cls.Terminated, + ] + + @classmethod + def failed_status(cls): + return [ + cls.InitializeFailed, + cls.Failed, + cls.CreateFailed, + ] + + +class UserVpcConfig(BaseAPIModel): + """UserVpcConfig represents the VPC configuration for the training job instance.""" + + vpc_id: str = Field( + ..., + description="Specifies the ID of the VPC that training job instance connects to.", + ) + security_group_id: str = Field( + ..., + description="The ID of the security group that training job instances belong to.", + ) + switch_id: Optional[str] = Field( + None, + description="The ID of the vSwitch to which the instance belongs. Defaults to None.", + ) + extended_cidrs: Optional[List[str]] = Field( + None, + description="The CIDR blocks configured for the ENI of the training job instance. " + "If it is not specified, the CIDR block will be configured as the same as the VPC " + "network segmentation, which means that the training job instance can access all " + "resources in the VPC. Defaults to None.", + ) + + +class ExperimentConfig(BaseAPIModel): + """ExperimentConfig is used to configure the experiment to which the job belongs.""" + + experiment_id: str = Field( + ..., + description="Specifies the ID of the experiment that training job instance belongs to.", + ) + + +class OssLocation(BaseAPIModel): + """OSS location.""" + + bucket: str = Field(..., description="OSS bucket name.") + key: str = Field(..., description="Object key in the OSS bucket.") + endpoint: Optional[str] = Field(None, description="OSS service endpoint URL.") + + +class CodeDir(BaseAPIModel): + """Source code location""" + + location_value: Union[OssLocation, Dict[str, Any]] = Field( + ..., description="Location of the code directory." + ) + location_type: str = Field( + ..., description="Type of the code directory location, e.g., OSS." + ) + + +# HyperParameter +class HyperParameter(BaseAPIModel): + """A hyperparameter for a training job.""" + + value: str = Field(..., description="Value of the hyperparameter.") + name: str = Field(..., description="Name of the hyperparameter.") + + +class InstanceSpec(BaseAPIModel): + """Instance resource configuration""" + + memory: str = Field(..., description="Memory allocation for the instance.") + cpu: str = Field(..., alias="CPU", description="CPU allocation for the instance.") + gpu: str = Field(..., alias="GPU", description="GPU allocation for the instance.") + shared_memory: Optional[str] = Field( + None, description="Shared memory allocation, if applicable." + ) + + +class ComputeResource(BaseAPIModel): + """Compute Resource Configuration.""" + + ecs_count: Optional[int] = Field(None, description="Number of ECS instances.") + ecs_spec: Optional[str] = Field(None, description="Specification of ECS instances.") + instance_count: Optional[int] = Field(None, description="Number of instances.") + instance_spec: Optional[InstanceSpec] = Field( + None, description="Specification for instances." + ) + + +# URI Input and Output +class UriInput(BaseAPIModel): + """URI Input for a training job.""" + + name: str = Field(..., description="Name of the input.") + input_uri: str = Field(..., description="URI of the input data.") + + +class UriOutput(BaseAPIModel): + """URI Output for a training job.""" + + name: str = Field(..., description="Name of the output.") + output_uri: str = Field(..., description="URI of the output data.") + + +class DatasetConfig(BaseAPIModel): + """Dataset Configuration""" + + dataset_id: str = Field(..., description="Unique ID of the dataset.") + name: Optional[str] = Field(None, description="Name of the dataset.") + dataset_name: Optional[str] = Field( + None, description="Alternative name of the dataset." + ) + + +class Channel(BaseAPIModel): + """Channel Configuration.""" + + name: str = Field(..., description="Name of the channel.") + description: Optional[str] = Field(None, description="Description of the channel.") + required: Optional[bool] = Field( + None, description="Indicates if the channel is required." + ) + supported_channel_types: Optional[List[str]] = Field( + None, description="Supported types for this channel." + ) + properties: Optional[Dict[str, Any]] = Field( + None, description="Additional properties of the channel." + ) + + +# HyperParameter Definition +class HyperParameterDefinition(BaseAPIModel): + """HyerParameter Definition.""" + + name: str = Field(..., description="Name of the hyperparameter.") + type: Optional[str] = Field(None, description="Type of the hyperparameter.") + default_value: Optional[str] = Field( + None, description="Default value of the hyperparameter." + ) + description: Optional[str] = Field( + None, description="Description of the hyperparameter." + ) + required: bool = Field( + False, description="Indicates if the hyperparameter is required." + ) + + +class SchedulerConfig(BaseAPIModel): + max_running_time_in_seconds: Optional[int] = None + + +class MetricDefinition(BaseAPIModel): + description: Optional[str] = Field(None, description="Description of the metric.") + name: str = Field(..., description="Name of the metric.") + regex: str = Field( + ..., description="Regular expression used for capturing the metric." + ) + + +class AlgorithmSpec(BaseAPIModel): + """Algorithm Specification.""" + + command: List[str] = Field(..., description="Command to run the training job.") + image: str = Field(..., description="Docker image for the training job.") + supported_channel_types: List[str] = Field(default_factory=list) + output_channels: List[Channel] = Field( + default_factory=list, description="Output channels." + ) + input_channels: List[Channel] = Field( + default_factory=list, description="Input channels." + ) + supports_distributed_training: Optional[bool] = Field( + True, description="Whether the algorithm supports distributed training." + ) + supported_instance_types: Optional[List[str]] = Field( + None, description="Supported instance types." + ) + metric_definitions: Optional[List[MetricDefinition]] = Field( + None, description="Metric definitions." + ) + hyperparameter_definitions: List[HyperParameterDefinition] = Field( + default_factory=list, + alias="HyperParameter", + description="Hyperparameter definitions.", + ) + job_type: str = Field(default="PyTorchJob") + code_dir: Optional[CodeDir] = Field(None, description="Source code location.") + + +class ModelTrainingSpec(BaseAPIModel): + compute_resource: Optional[ComputeResource] = None + hyperparameters: List[HyperParameter] = Field( + default_factory=list, alias="HyperParameters" + ) + inputs: List[Union[UriInput, DatasetConfig]] = Field( + default_factory=list, alias="InputChannels" + ) + scheduler: Optional[SchedulerConfig] = None + supported_instance_types: Optional[List[str]] = None + algorithm_spec: Optional[AlgorithmSpec] = None + algorithm_version: Optional[str] = None + algorithm_provider: Optional[str] = None + algorithm_name: Optional[str] = None + environments: Optional[Dict[str, str]] = None + requirements: Optional[List[str]] = None + + +ModelEvaluationSpec = ModelTrainingSpec + + +class TrainingJob(BaseAPIModel): + """TrainingJob represents a training job in the PAI service.""" + + algorithm_id: Optional[str] = None + algorithm_name: Optional[str] = None + algorithm_provider: Optional[str] = None + algorithm_version: Optional[str] = None + algorithm_spec: Optional[AlgorithmSpec] = None + compute_resource: Optional[ComputeResource] = None + scheduler: Optional[SchedulerConfig] = None + experiment_config: Optional[Dict[str, Any]] = None + inputs: List[Union[UriInput, DatasetConfig]] = Field( + default=list, alias="InputChannels" + ) + outputs: List[Union[UriOutput, DatasetConfig]] = Field( + default=list, alias="OutputChannels" + ) + hyperparameters: List[HyperParameter] = Field( + default_factory=list, alias="HyperParameters" + ) + labels: Optional[List[Dict[str, str]]] = Field(default_factory=list) + training_job_description: Optional[str] = None + training_job_id: Optional[str] = None + training_job_name: Optional[str] = None + workspace_id: Optional[str] = None + training_job_url: Optional[str] = None + status: Optional[str] = None + reason_code: Optional[str] = None + reason_message: Optional[str] = None + + def __hash__(self): + return hash(self.training_job_id) + + def __eq__(self, other: "TrainingJob"): + return ( + isinstance(other, TrainingJob) + and self.training_job_id == other.training_job_id + ) + + @property + def id(self): + return self.training_job_id + + @classmethod + def get(cls, training_job_id, session: Session = None) -> "TrainingJob": + session = session or get_default_session() + res = session.training_job_api.get(training_job_id=training_job_id) + return cls.model_validate(res) + + @classmethod + def list( + cls, + status: Optional[str] = None, + session: Optional[Session] = None, + page_size: int = 50, + page_number: int = 1, + ): + session = session or get_default_session() + res = session.training_job_api.list( + status=status, page_size=page_size, page_number=page_number + ) + return [cls.model_validate(item) for item in res.items] + + def output_path(self, channel_name="model"): + for output_channel in self.outputs: + if output_channel.name == channel_name: + return output_channel.output_uri + raise RuntimeError( + f"Output channel is not specified: channel_name={channel_name}" + ) + + @property + def console_uri(self): + if not self.training_job_id: + raise ValueError("The TrainingJob is not submitted") + + return self.training_job_url + + def wait(self, interval: int = 5, show_logs: bool = True): + session = get_default_session() + self._refresh_status() + + if show_logs: + job_log_printer = _TrainingJobLogPrinter( + training_job_id=self.training_job_id, page_size=20, session=session + ) + job_log_printer.start() + else: + job_log_printer = None + try: + while not self.is_completed(): + time.sleep(interval) + finally: + if job_log_printer: + job_log_printer.stop(wait=True) + + self._on_job_completed() + + def _on_job_completed(self): + # Print an empty line to separate the training job logs and the following logs + print() + if self.status == TrainingJobStatus.Succeed: + print( + f"Training job ({self.training_job_id}) succeeded, you can check the" + f" logs/metrics/output in the console:\n{self.console_uri}" + ) + elif self.status == TrainingJobStatus.Terminated: + print( + f"Training job is ended with status {self.status}: " + f"reason_code={self.reason_code}, reason_message={self.reason_message}." + f"Check the training job in the console:\n{self.console_uri}" + ) + elif self.status in TrainingJobStatus.failed_status(): + print( + f"Training job ({self.training_job_id}) failed, please check the logs" + f" in the console: \n{self.console_uri}" + ) + + message = f"TrainingJob failed: name={self.training_job_name}, " + f"training_job_id={self.training_job_id}, " + f"reason_code={self.reason_code}, status={self.status}, " + f"reason_message={self.reason_message}" + + raise UnexpectedStatusException(message=message, status=self.status) + + def _refresh_status(self): + """Reload the training job from the PAI Service,""" + session = get_default_session() + training_job = type(self).model_validate( + session.training_job_api.get(training_job_id=self.training_job_id) + ) + self.status = training_job.status + + def is_succeeded(self): + """Return True if the training job is succeeded""" + self._refresh_status() + return self.status == TrainingJobStatus.Succeed + + @retry(wait_secs=10) + def is_completed(self): + """Return True if the training job is completed, including failed status""" + if self.status in TrainingJobStatus.completed_status(): + return True + self._refresh_status() + + return self.status in TrainingJobStatus.completed_status() + + +class _TrainingJobLogPrinter(object): + """A class used to print logs for a training job""" + + executor = ThreadPoolExecutor(5) + + def __init__( + self, training_job_id: str, page_size=10, session: Optional[Session] = None + ): + self.training_job_id = training_job_id + self.session = session + self.page_size = page_size + self._future = None + self._stop = False + + def _list_logs_api(self, page_number: int = 1): + try: + res = self.session.training_job_api.list_logs( + self.training_job_id, + page_number=page_number, + page_size=self.page_size, + ) + return res + except TeaException as e: + # hack: Backend service may raise an exception when the training job + # instance is not found. + if e.code == "TRAINING_JOB_INSTANCE_NOT_FOUND": + return PaginatedResult(items=[], total_count=0) + else: + raise e + + def _list_logs(self): + page_number, page_offset = 1, 0 + # print training job logs. + while not self._stop: + res = self._list_logs_api(page_number=page_number) + # 1. move to next page + if len(res.items) == self.page_size: + # print new logs starting from page_offset + self._print_logs(logs=res.items[page_offset:]) + page_number += 1 + page_offset = 0 + # 2. stay at the current page. + else: + if len(res.items) > page_offset: + # print new logs starting from page_offset + self._print_logs(logs=res.items[page_offset:]) + page_offset = len(res.items) + time.sleep(1) + + # When _stop is True, wait and print remaining logs. + time.sleep(10) + while True: + res = self._list_logs_api(page_number=page_number) + # There maybe more logs in the next page + if len(res.items) == self.page_size: + self._print_logs(logs=res.items[page_offset:]) + page_number += 1 + page_offset = 0 + # No more logs in the next page. + else: + if len(res.items) > page_offset: + self._print_logs(logs=res.items[page_offset:]) + break + + def _print_logs(self, logs: List[str]): + for log in logs: + print(log) + + def start(self): + if self._future: + raise ValueError("The training job log printer is already started") + self._stop = False + self._future = self.executor.submit(self._list_logs) + + def stop(self, wait: bool = True): + self._stop = True + if self._future: + self._future.result() + + +class _TrainingJobSubmitter(object): + """A class used to submit a training job to the PAI service.""" + + def __init__( + self, + base_job_name: Optional[str] = None, + output_path: Optional[str] = None, + experiment_config: Optional[ExperimentConfig] = None, + user_vpc_config: Optional[UserVpcConfig] = None, + max_run_time: Optional[int] = None, + instance_type: Optional[str] = None, + instance_spec: Optional[Dict] = None, + instance_count: Optional[int] = None, + resource_id: Optional[Dict] = None, + environments: Optional[Dict] = None, + requirements: Optional[List[str]] = None, + labels: Optional[Dict[str, str]] = None, + ): + self.session = get_default_session() + self._training_jobs = [] + self.base_job_name = base_job_name or type(self).__name__.lower() + self.output_path = output_path + self.user_vpc_config = user_vpc_config + self.experiment_config = experiment_config + self.max_run_time = max_run_time + self.instance_type = instance_type + self.instance_spec = instance_spec + self.instance_count = instance_count or 1 + self.resource_id = resource_id + self.environments = environments + self.requirements = requirements + self.labels = labels + + def wait(self, interval: int = 5, show_logs: bool = True, all_jobs: bool = False): + """Block until the jobs is completed. + + Args: + interval(int): Interval to reload job status + show_logs(bool): Specifies whether to fetch and print the logs produced by + the job. + all_jobs(bool): Wait latest job or wait all jobs in processor, show_logs disabled while + wait all jobs. + + Raises: + RuntimeError: If no job is submitted. + + """ + if all_jobs: + if not self._training_jobs: + raise RuntimeError("Could not find any submitted job.") + remains = set(self._training_jobs) + while remains: + for job in self._training_jobs: + if job in remains and job.is_completed(): + remains.remove(job) + + time.sleep(interval) + self._generate_jobs_report() + else: + latest_job = self.latest_job + if not latest_job: + raise RuntimeError("Could not find a submitted job.") + latest_job.wait(interval=interval, show_logs=show_logs) + return latest_job + + def _generate_jobs_report(self): + """Generate current jobs report and output to stdout""" + print(f"Jobs status report, total jobs count: {len(self._training_jobs)}") + rows = [] + headers = ["JobName", "JobID", "Status"] + for job in self._training_jobs: + rows.append([job.training_job_name, job.id, job.status]) + print_table(headers, rows) + + def job_name(self, job_name: Optional[str] = None): + if job_name: + return job_name + sep = "-" + base_name = self.base_job_name + return name_from_base(base_name, sep) + + def build_inputs( + self, + inputs: Dict[str, Any], + input_channels: List[Channel], + default_inputs: List[Union[DatasetConfig, UriInput]] = None, + ) -> List[Dict[str, str]]: + res = [] + inputs = inputs or dict() + input_channels = input_channels or [] + default_inputs = default_inputs or [] + + input_keys = set(list(inputs.keys()) + [item.name for item in default_inputs]) + + requires = {ch.name for ch in input_channels if ch.required} - input_keys + if requires: + raise ValueError( + "Required input channels are not provided: {}".format( + ",".join(requires) + ) + ) + for name, item in inputs.items(): + input_config = self._get_input_config(name, item) + res.append(input_config.model_dump()) + + for item in default_inputs: + res.append(item.model_dump()) + + return res + + @staticmethod + def _default_training_output_channels() -> List[Channel]: + channels = [ + Channel( + name=DEFAULT_OUTPUT_MODEL_CHANNEL_NAME, + description="Training output models", + required=True, + ), + Channel( + name=DEFAULT_CHECKPOINT_CHANNEL_NAME, + description="Training checkpoints channel", + required=False, + ), + Channel( + name=DEFAULT_TENSORBOARD_CHANNEL_NAME, + properties={"ossAppendable": "true"}, + description="TensorBoard logs channel", + required=False, + ), + ] + + return channels + + def _training_job_base_output(self, job_name): + job_name = to_plain_text(job_name) + if self.output_path: + if not is_oss_uri(self.output_path): + raise ValueError("Output path should be an OSS path.") + return os.path.join(self.output_path, f"{job_name}_{random_str(6)}") + + session = get_default_session() + bucket_name = session.oss_bucket.bucket_name + storage_path = session.get_storage_path_by_category( + StoragePathCategory.TrainingJob, + f"{to_plain_text(job_name)}_{random_str(6)}", + ) + base_output_path = f"oss://{bucket_name}/{storage_path}" + return base_output_path + + def build_outputs( + self, + job_name: str, + output_channels: List[Channel], + outputs: Optional[Dict[str, Any]] = None, + ) -> List[Dict[str, str]]: + base_output_path = self._training_job_base_output(job_name) + res = [] + outputs = outputs or dict() + + for ch in output_channels: + if ch.name in outputs: + output = self._get_output_config(name=ch.name, item=outputs[ch.name]) + else: + output_uri = as_oss_dir_uri(posixpath.join(base_output_path, ch.name)) + output = UriOutput(name=ch.name, output_uri=output_uri) + res.append(output) + + extra_outputs = set(outputs.keys()) - {ch.name for ch in output_channels} + + for name in extra_outputs: + output = self._get_output_config( + name=name, + item=outputs[name], + ) + res.append(output) + + return [item.model_dump() for item in res] + + def _submit( + self, + job_name: str, + algorithm_spec: Optional[AlgorithmSpec] = None, + algorithm_name: Optional[str] = None, + algorithm_version: Optional[str] = None, + algorithm_provider: Optional[str] = None, + instance_count: int = 1, + instance_type: Optional[str] = None, + instance_spec: Optional[InstanceSpec] = None, + resource_id: Optional[str] = None, + inputs: Optional[List[Dict[str, Any]]] = None, + outputs: Optional[List[Dict[str, Any]]] = None, + hyperparameters: Optional[Dict[str, str]] = None, + max_run_time: Optional[int] = None, + environments: Optional[Dict[str, str]] = None, + user_vpc_config: Optional[Dict[str, str]] = None, + requirements: Optional[List[str]] = None, + experiment_config: Optional[Dict[str, Any]] = None, + labels: Optional[Dict[str, str]] = None, + wait: bool = True, + show_logs: bool = False, + ): + session = get_default_session() + training_job_id = session.training_job_api.create( + instance_count=instance_count, + instance_spec=instance_spec.model_dump() if instance_spec else None, + algorithm_name=algorithm_name, + algorithm_provider=algorithm_provider, + experiment_config=( + experiment_config.model_dump() + if experiment_config and isinstance(experiment_config, ExperimentConfig) + else experiment_config + ), + algorithm_version=algorithm_version, + instance_type=instance_type, + resource_id=resource_id, + job_name=job_name, + hyperparameters=hyperparameters, + max_running_in_seconds=max_run_time, + input_channels=inputs, + output_channels=outputs, + algorithm_spec=algorithm_spec.model_dump() if algorithm_spec else None, + requirements=requirements, + user_vpc_config=user_vpc_config, + labels=labels, + environments=environments, + ) + training_job = TrainingJob.get(training_job_id) + self._training_jobs.append(training_job) + print( + f"View the job detail by accessing the console URI: {training_job.console_uri}" + ) + if wait: + training_job.wait(show_logs=show_logs) + + @classmethod + def _get_input_config( + cls, name: str, item: Union[str, "FileSystemInputBase", DatasetConfig] + ): + """Get input uri for training_job from given input.""" + from pai.estimator import FileSystemInputBase + + if not isinstance(item, (str, FileSystemInputBase, DatasetConfig)): + raise ValueError(f"Input data of type {type(item)} is not supported.") + + if isinstance(item, FileSystemInputBase): + input_ = UriInput( + name=name, + input_uri=item.to_input_uri(), + ) + elif isinstance(item, DatasetConfig): + input_ = DatasetConfig( + name=name, + dataset_id=item.dataset_id, + ) + elif is_oss_uri(item) or is_filesystem_uri(item) or is_odps_table_uri(item): + input_ = UriInput( + name=name, + input_uri=item, + ) + elif os.path.exists(item): + store_path = Session.get_storage_path_by_category( + StoragePathCategory.InputData + ) + input_ = UriInput(name=name, input_uri=upload(item, store_path)) + elif is_dataset_id(item): + input_ = DatasetConfig( + dataset_id=item, + name=name, + ) + else: + raise ValueError( + "Invalid input data, supported inputs are OSS, NAS, MaxCompute " + "table or local path." + ) + return input_ + + @classmethod + def _get_output_config( + cls, name: str, item: str + ) -> Union[UriOutput, DatasetConfig]: + from pai.estimator import FileSystemInputBase + + if not isinstance(item, (str, FileSystemInputBase, DatasetConfig)): + raise ValueError(f"Output data of type {type(item)} is not supported.") + + if isinstance(item, FileSystemInputBase): + output = UriOutput( + name=name, + output_uri=item.to_input_uri(), + ) + elif isinstance(item, DatasetConfig): + output = DatasetConfig(name=name, dataset_id=item.dataset_id) + elif is_oss_uri(item) or is_filesystem_uri(item) or is_odps_table_uri(item): + output = UriOutput( + name=name, + output_uri=as_oss_dir_uri(item), + ) + else: + raise ValueError( + "Invalid output data, supported outputs are OSS, NAS, MaxCompute " + ) + + return output + + @property + def latest_job(self) -> "TrainingJob": + return self._training_jobs[-1] if self._training_jobs else None + + def _build_code_input( + self, job_name: str, source_dir: Optional[str], code_dest: Optional[str] = None + ) -> Optional[CodeDir]: + """Upload source files to OSS and return the code input for training job.""" + if not source_dir: + return + if is_oss_uri(source_dir): + code_uri = source_dir + elif not os.path.exists(source_dir): + raise ValueError(f"Source directory {source_dir} does not exist.") + else: + code_dest = code_dest or self.session.get_storage_path_by_category( + StoragePathCategory.TrainingSrc, to_plain_text(job_name) + ) + code_uri = upload( + source_path=source_dir, + oss_path=code_dest, + bucket=self.session.oss_bucket, + is_tar=True, + ) + oss_uri_obj = OssUriObj(uri=self.session.patch_oss_endpoint(code_uri)) + code_dir = CodeDir( + location_type="oss", + location_value=OssLocation( + bucket=oss_uri_obj.bucket_name, + key=oss_uri_obj.object_key, + endpoint=oss_uri_obj.endpoint, + ), + ) + + return code_dir diff --git a/pai/model.py b/pai/model.py index c5065b9..52c9a7a 100644 --- a/pai/model.py +++ b/pai/model.py @@ -30,8 +30,7 @@ from oss2 import ObjectIterator from .common import ProviderAlibabaPAI, git_utils -from .common.configs import UserVpcConfig -from .common.consts import INSTANCE_TYPE_LOCAL_GPU, ModelFormat +from .common.consts import INSTANCE_TYPE_LOCAL_GPU, ModelFormat, StoragePathCategory from .common.docker_utils import ContainerRun, run_container from .common.logging import get_logger from .common.oss_utils import OssUriObj, download, is_oss_uri, upload @@ -43,6 +42,7 @@ ) from .exception import DuplicatedMountException from .image import ImageInfo +from .job import InstanceSpec, ModelTrainingSpec, UriInput, UserVpcConfig from .predictor import AsyncPredictor, LocalPredictor, Predictor, ServiceType from .serializers import SerializerBase from .session import Session, get_default_session @@ -276,7 +276,9 @@ def _upload_source_dir(cls, source_dir, session): f"Input source code path should be a directory: {source_dir}." ) - target_dir = session.get_storage_path_by_category(category="inference_src") + target_dir = session.get_storage_path_by_category( + category=StoragePathCategory.InferenceSrc + ) # upload local script data to the OSS bucket. uploaded_source_code = upload( source_dir, @@ -358,7 +360,9 @@ def mount( elif os.path.exists(source): # if source is a local path, upload it to OSS bucket and use OSS URI # as storage source. - oss_path = session.get_storage_path_by_category("model_data") + oss_path = session.get_storage_path_by_category( + StoragePathCategory.ModelData + ) oss_uri = upload( source_path=source, oss_path=oss_path, bucket=session.oss_bucket ) @@ -589,12 +593,14 @@ def container_serving_spec( "image": image_uri, "port": port, "script": command, - "env": [ - {"name": key, "value": str(value)} - for key, value in environment_variables.items() - ] - if environment_variables - else [], + "env": ( + [ + {"name": key, "value": str(value)} + for key, value in environment_variables.items() + ] + if environment_variables + else [] + ), } if health_check: @@ -757,7 +763,9 @@ def _upload_model_data(self): elif not os.path.exists(self.model_data): raise RuntimeError(f"Model data path does not exist: {self.model_data}") - dest_oss_path = self.session.get_storage_path_by_category(category="model_data") + dest_oss_path = self.session.get_storage_path_by_category( + category=StoragePathCategory.ModelData + ) upload_model_data = upload( source_path=self.model_data, oss_path=dest_oss_path, @@ -1229,9 +1237,9 @@ def register( framework_type=framework_type, training_spec=training_spec, evaluation_spec=evaluation_spec, - inference_spec=self.inference_spec.to_dict() - if self.inference_spec - else None, + inference_spec=( + self.inference_spec.to_dict() if self.inference_spec else None + ), approval_status=approval_status, metrics=metrics, options=options, @@ -1832,6 +1840,36 @@ def _build_service_config( return inference_spec.to_dict() + def get_training_spec(self, training_method: Optional[str]) -> ModelTrainingSpec: + if type(self)._is_multiple_spec(self.training_spec): + supported_training_methods = list(self.training_spec.keys()) + if training_method and training_method not in supported_training_methods: + raise ValueError( + "The model does not support the given training method:" + f" {training_method}. Supported training methods are:" + f" {supported_training_methods}." + ) + elif training_method: + ts = self.training_spec.get(training_method) + else: + training_method = supported_training_methods[0] + logger.warning( + "The training method is not specified, using the default training" + " method: %s. Supported training methods are: %s.", + training_method, + supported_training_methods, + ) + ts = self.training_spec.get(training_method) + else: + # Does not support training methods. # Use default training spec. + if training_method: + raise ValueError( + "The model does not support choosing training method. Do not" + " specify the training method." + ) + ts = self.training_spec + return ModelTrainingSpec.model_validate(ts) + def get_estimator( self, training_method: Optional[str] = None, @@ -1848,7 +1886,7 @@ def get_estimator( Generate an AlgorithmEstimator object from RegisteredModel's training_spec. Args: - training_method (str, optional): Used to selected the training algorithm + training_method (str, optional): Used to select the training algorithm that supported by the model. If not specified, the default training algorithm will be retrieved from the model version. instance_type (str, optional): The machine instance type used to run the @@ -1881,59 +1919,14 @@ def get_estimator( raise ValueError( "The provided registered model does not contain training spec." ) - ts = self.training_spec - if "AlgorithmSpec" not in ts and "AlgorithmName" not in ts: - # Support choosing training methods. - supported_training_methods = list(ts.keys()) - if training_method and training_method not in supported_training_methods: - raise ValueError( - "The model does not support the given training method:" - f" {training_method}. Supported training methods are:" - f" {supported_training_methods}." - ) - elif training_method: - ts = ts.get(training_method) - else: - training_method = supported_training_methods[0] - logger.warning( - "The training method is not specified, using the default training" - " method: %s. Supported training methods are: %s.", - training_method, - supported_training_methods, - ) - ts = ts.get(training_method) - else: - # Does not support training methods. - # Use default training spec. - if training_method: - raise ValueError( - "The model does not support choosing training method. Do not" - " specify the training method." - ) - - if "AlgorithmSpec" not in ts and "AlgorithmName" not in ts: - raise ValueError( - "The provided registered model's training spec does not contain any" - " algorithms." - ) - if "AlgorithmSpec" in ts: - algorithm_spec = ts.get("AlgorithmSpec") - algorithm_name, algorithm_provider, algorithm_version = (None, None, None) - else: - algorithm_name, algorithm_provider, algorithm_version = ( - ts.get("AlgorithmName"), - ts.get("AlgorithmProvider"), - ts.get("AlgorithmVersion"), - ) - algorithm_spec = None - + ts = self.get_training_spec(training_method=training_method) hyperparameters = hyperparameters or {} # TODO: validate the given hyperparameters via algorithm definition - for hp in ts.get("HyperParameters", []): - if hp["Name"] not in hyperparameters: + for hp in ts.hyperparameters: + if hp.name not in hyperparameters: hyperparameters.update( { - hp["Name"]: hp["Value"], + hp.name: hp.value, } ) @@ -1941,18 +1934,42 @@ def get_estimator( base_job_name = f"{self.model_name}_training" if self.model_name else None if not max_run_time: - max_run_time = ts.get("Scheduler", {}).get("MaxRunningTimeInSeconds") + max_run_time = ( + ts.scheduler.max_running_time_in_seconds if ts.scheduler else None + ) - train_compute_resource = ts.get("ComputeResource") + resource_id = kwargs.get("resource_id") instance_spec = kwargs.get("instance_spec") - if train_compute_resource: - instance_type = instance_type or train_compute_resource.get("EcsSpec") + compute_resource = ts.compute_resource + if resource_id: + if instance_type: + logger.warning( + "The instance type is ignored when resource_id is provided." + ) + instance_spec = instance_type or compute_resource.instance_spec + if not instance_spec: + raise ValueError( + "Instance spec is required when resource_id is provided." + ) + instance_spec = InstanceSpec.model_validate(instance_spec) + instance_count = ( + instance_count + or compute_resource.instance_count + or compute_resource.ecs_count + or 1 + ) + else: + if instance_spec: + logger.warning( + "The instance spec is ignored when resource_id is not provided." + ) + instance_type = instance_type or compute_resource.ecs_spec instance_count = ( instance_count - or train_compute_resource.get("EcsCount") - or train_compute_resource.get("InstanceCount") + or compute_resource.ecs_count + or compute_resource.instance_count + or 1 ) - instance_spec = instance_spec or train_compute_resource.get("InstanceSpec") labels = kwargs.pop("labels", dict()) if self.model_provider == ProviderAlibabaPAI: @@ -1969,10 +1986,10 @@ def get_estimator( labels = default_labels return AlgorithmEstimator( - algorithm_name=algorithm_name, - algorithm_version=algorithm_version, - algorithm_provider=algorithm_provider, - algorithm_spec=algorithm_spec, + algorithm_name=ts.algorithm_name, + algorithm_version=ts.algorithm_version, + algorithm_provider=ts.algorithm_provider, + algorithm_spec=ts.algorithm_spec, hyperparameters=hyperparameters, base_job_name=base_job_name, max_run_time=max_run_time, @@ -1984,35 +2001,26 @@ def get_estimator( **kwargs, ) - def get_estimator_inputs(self) -> Dict[str, str]: + def get_estimator_inputs(self, training_method=None) -> Dict[str, Any]: """Get the AlgorithmEstimator's default input channels Get the AlgorithmEstimator's default input channels from RegisteredModel's training_spec. Returns: - dict[str, str]: A dict of input channels. + Dict[str, str]: A dict of input channels. """ - if not self.training_spec: - raise ValueError( - "The provided registered model does not contain training spec." - ) - ts = self.training_spec - if "AlgorithmSpec" not in ts and "AlgorithmName" not in ts: - raise ValueError( - "The provided registered model's training spec does not contain any" - " algorithms." - ) + default_inputs = ( + self.get_training_spec(training_method=training_method).inputs or [] + ) - input_channels = {} - if "InputChannels" in ts: - for i in ts["InputChannels"]: - input_channels.update( - { - i["Name"]: i["InputUri"], - } - ) - return input_channels + ret = {} + for item in default_inputs: + if isinstance(item, UriInput): + ret[item.name] = item.input_uri + else: + ret[item.name] = item + return ret def get_eval_processor( self, @@ -2066,47 +2074,44 @@ def get_eval_processor( raise ValueError( "The provided registered model does not contain evaluation spec." ) - - if "AlgorithmSpec" not in eval_spec: + eval_spec = ModelTrainingSpec.model_validate(eval_spec) + if not eval_spec.algorithm_spec: raise ValueError( - "The provided registered model's evaluation spec does not contain any" - " workload." + "Invalid evaluation spec, the evaluation spec does not contain any" + " configuration for the evaluation job." ) - workload = eval_spec.get("AlgorithmSpec") + # workload = eval_spec.get("AlgorithmSpec") if not base_job_name: base_job_name = f"{self.model_name}_eval" if self.model_name else None parameters = parameters or dict() - for item in eval_spec.get("HyperParameters"): - name = item["Name"] - value = item["Value"] - if name not in parameters: - parameters[name] = value + for item in eval_spec.hyperparameters: + if item.name not in parameters: + parameters[item.name] = item.value if not max_run_time: - max_run_time = eval_spec.get("Scheduler", {}).get("MaxRunningTimeInSeconds") + max_run_time = eval_spec.scheduler.max_running_time_in_seconds - compute_resource = eval_spec.get("ComputeResource") + compute_resource = eval_spec.compute_resource if compute_resource and (not instance_type or not instance_count): # If instance_type or instance_count is not provided, use the default - instance_type = instance_type or compute_resource.get("EcsSpec") - instance_count = instance_count or compute_resource.get("EcsCount") + instance_type = instance_type or compute_resource.ecs_spec + instance_count = instance_count or compute_resource.ecs_count source_dir = None - code_dir = workload.get("CodeDir") - if code_dir and code_dir.get("LocationType") == "oss": - location = code_dir.get("LocationValue") - oss_path = OssUriObj.from_bucket_key_endpoint( - bucket_name=location.get("Bucket"), - object_key=location.get("Key"), - endpoint=location.get("Endpoint"), - ) - source_dir = oss_path.uri + code_dir = eval_spec.algorithm_spec.code_dir + if code_dir and code_dir.location_type == "oss": + oss_uri_obj = OssUriObj.from_bucket_key_endpoint( + bucket_name=code_dir.location_value.bucket, + object_key=code_dir.location_value.key, + endpoint=code_dir.location_value.endpoint, + ) + source_dir = oss_uri_obj.uri processor = Processor( - image_uri=workload.get("Image"), - command=" ".join(workload.get("Command")), + image_uri=eval_spec.algorithm_spec.image, + command=eval_spec.algorithm_spec.command, source_dir=source_dir, parameters=parameters, max_run_time=max_run_time, @@ -2117,8 +2122,8 @@ def get_eval_processor( user_vpc_config=user_vpc_config, session=self.session, ) - processor.set_input_channel_definitions(workload["InputChannels"]) - processor.set_output_channel_definitions(workload["OutputChannels"]) + processor.set_input_channels(eval_spec.algorithm_spec.input_channels) + processor.set_output_channels(eval_spec.algorithm_spec.output_channels) return processor @@ -2135,17 +2140,17 @@ def get_evaluation_inputs(self) -> Dict[str, Any]: raise ValueError( "The provided registered model does not contain evaluation spec." ) + eval_spec = ModelTrainingSpec.model_validate(self.evaluation_spec) + inputs = eval_spec.inputs or [] + res = {} - input_channels = {} - if "InputChannels" in self.evaluation_spec: - for i in self.evaluation_spec["InputChannels"]: - input_channels.update( - { - i["Name"]: i.get("InputUri") or i.get("DatasetId"), - } - ) + for item in inputs: + res[item.name] = item.input_uri if isinstance(item, UriInput) else item + return res - return input_channels + @classmethod + def _is_multiple_spec(cls, spec: Dict[str, Any]) -> bool: + return not ("AlgorithmSpec" in spec or "AlgorithmName" in spec) def _get_evaluation_spec(self): """Get the evaluation_spec of the registered model.""" diff --git a/pai/processor.py b/pai/processor.py index a0e2bf5..e73b085 100644 --- a/pai/processor.py +++ b/pai/processor.py @@ -12,130 +12,28 @@ # See the License for the specific language governing permissions and # limitations under the License. import os -import posixpath -import time from datetime import datetime from typing import Any, Dict, List, Optional, Union -from .common.configs import UserVpcConfig from .common.consts import JobType, StoragePathCategory from .common.logging import get_logger -from .common.oss_utils import OssUriObj, is_oss_uri, upload -from .common.utils import ( - experimental, - is_dataset_id, - is_filesystem_uri, - is_odps_table_uri, - random_str, - to_plain_text, +from .common.utils import experimental, random_str, to_plain_text +from .job import ( + AlgorithmSpec, + Channel, + CodeDir, + ExperimentConfig, + TrainingJob, + UserVpcConfig, + _TrainingJobSubmitter, ) -from .estimator import FileSystemInputBase -from .estimator import _TrainingJob as _Job -from .experiment import ExperimentConfig from .session import Session, get_default_session logger = get_logger(__name__) -def build_code_input( - source_dir: str, upload_data_path: str -) -> Optional[Dict[str, Any]]: - """Upload local code and build CodeDir config for job.""" - if not source_dir: - return - - from pai.session import get_default_session - - sess = get_default_session() - - if is_oss_uri(source_dir): - code_oss_uri = source_dir - elif os.path.exists(source_dir): - code_oss_uri = upload( - source_path=source_dir, - oss_path=upload_data_path, - bucket=sess.oss_bucket, - is_tar=True, - ) - else: - raise ValueError(f"Source directory {source_dir} does not exist.") - - code_oss_obj = OssUriObj(uri=sess.patch_oss_endpoint(code_oss_uri)) - res = { - "LocationType": "oss", - "LocationValue": { - "Bucket": code_oss_obj.bucket_name, - "Key": code_oss_obj.object_key, - "Endpoint": code_oss_obj.endpoint, - }, - } - - return res - - -def get_input_channel_config( - item: Optional[Union[str, FileSystemInputBase]] -) -> Dict[str, str]: - """Get channel config from given job input.""" - - if not isinstance(item, (str, FileSystemInputBase)): - raise ValueError(f"Input data of type {type(item)} is not supported.") - - if isinstance(item, FileSystemInputBase): - config = {"InputUri": item.to_input_uri()} - elif is_oss_uri(item) or is_filesystem_uri(item) or is_odps_table_uri(item): - config = {"InputUri": item} - elif is_dataset_id(item): - config = {"DatasetId": item} - elif os.path.exists(item): - store_path = Session.get_storage_path_by_category(StoragePathCategory.InputData) - config = {"InputUri": upload(item, store_path)} - else: - raise ValueError( - "Invalid input data, supported inputs are OSS, NAS, MaxCompute " - "table or local path." - ) - - return config - - -def get_output_channel_config( - item: Optional[Union[str, FileSystemInputBase]] -) -> Dict[str, str]: - """Get channel config from given job output.""" - - if not isinstance(item, (str, FileSystemInputBase)): - raise ValueError(f"Output data of type {type(item)} is not supported.") - - # OSS URI for output channel will be mounted to directory - # "/ml/output/{ChannelName}/" and the output OSS URI should be a "directory" - def as_oss_dir_uri(uri: str): - folder_uri = uri if uri.endswith("/") else uri + "/" - if folder_uri != uri: - logger.warning( - f"This output URI {uri} is not in the format of a folder path, " - f"system will automatically use {folder_uri} instead." - ) - return folder_uri - - if isinstance(item, FileSystemInputBase): - config = {"OutputUri": item.to_input_uri()} - elif is_oss_uri(item): - config = {"OutputUri": as_oss_dir_uri(item)} - elif is_filesystem_uri(item) or is_odps_table_uri(item): - config = {"OutputUri": item} - elif is_dataset_id(item): - config = {"DatasetId": item} - else: - raise ValueError( - "Invalid output data, supported inputs are OSS, NAS, MaxCompute table." - ) - - return config - - @experimental -class Processor(object): +class Processor(_TrainingJobSubmitter): def __init__( self, image_uri: str, @@ -267,33 +165,30 @@ def __init__( self.source_dir = source_dir self.job_type = job_type or JobType.PyTorchJob self.parameters = parameters or dict() - self.environments = environments - self.requirements = requirements - self.max_run_time = max_run_time - - self.base_job_name = base_job_name - self.output_path = output_path - - self.instance_type = instance_type - self.instance_count = instance_count or 1 - self.labels = labels - self.user_vpc_config = user_vpc_config - self.experiment_config = experiment_config self.session = session or get_default_session() - self._latest_job = None - self._jobs = [] - - self._input_channel_definitions = None - self._output_channel_definitions = None + self._input_channels = None + self._output_channels = None + super().__init__( + base_job_name=base_job_name, + output_path=output_path, + experiment_config=experiment_config, + instance_type=instance_type, + instance_count=instance_count or 1, + user_vpc_config=user_vpc_config, + max_run_time=max_run_time, + environments=environments, + requirements=requirements, + labels=labels, + ) def run( self, inputs: Dict[str, Any] = None, outputs: Dict[str, Any] = None, wait: bool = True, - show_logs=True, - ): + show_logs: bool = True, + ) -> TrainingJob: """Submit a job with the given input and output channels. Args: @@ -311,6 +206,10 @@ def run( either succeeded, failed, or stopped. (Default True). show_logs (bool): Specifies whether to show the logs produced by the job (Default True). + + Returns: + :class:`pai.job.TrainingJob`: A submitted training job. + Raises: UnExpectedStatusException: If the job fails. @@ -319,140 +218,71 @@ def run( outputs = outputs or dict() job_name = self._gen_job_display_name() - job = self._fit(inputs=inputs, outputs=outputs, job_name=job_name) - self._latest_job = job - self._jobs.append(job) - - if wait: - self.wait(show_logs=show_logs) - - def _gen_job_display_name(self, job_name=None): - """Generate job display name.""" - if job_name: - return job_name - ts = datetime.now().strftime("%Y%m%d_%H%M%S") - return "{}_{}".format(self.base_job_name or "processing_job", ts) - - def _build_algorithm_spec(self, code_input) -> Dict[str, Any]: - """Build a temporary AlgorithmSpec used for submitting the Job.""" - command = ( - self.command - if isinstance(self.command, list) - else [ - "/bin/sh", - "-c", - self.command, - ] - ) - - algo_spec = { - "Command": command, - "Image": self.image_uri, - "JobType": self.job_type, - "CodeDir": code_input, - "InputChannels": self._input_channel_definitions or None, - "OutputChannels": self._output_channel_definitions or None, - } - - return algo_spec - - def _fit( - self, job_name, inputs: Dict[str, Any] = None, outputs: Dict[str, Any] = None - ): - output_path = self._get_job_base_output_path(job_name) - upload_path = Session.get_storage_path_by_category( + code_dest = Session.get_storage_path_by_category( StoragePathCategory.ProcessingSrc, to_plain_text(job_name) ) - - input_configs = self._build_input_data_configs(inputs) - output_configs = self._build_output_data_configs(output_path, outputs) - + code_dir = self._build_code_input(job_name, self.source_dir, code_dest) algo_spec = self._build_algorithm_spec( - code_input=build_code_input(self.source_dir, upload_path), + code_input=code_dir, + ) + inputs = self.build_inputs(inputs, input_channels=algo_spec.input_channels) + outputs = self.build_outputs( + job_name=job_name, + output_channels=algo_spec.output_channels, + outputs=outputs, ) - job_id = self.session.training_job_api.create( + return self._submit( instance_count=self.instance_count, instance_type=self.instance_type, job_name=job_name, hyperparameters=self.parameters, environments=self.environments, requirements=self.requirements, - max_running_in_seconds=self.max_run_time, - input_channels=input_configs, - output_channels=output_configs, + max_run_time=self.max_run_time, + inputs=inputs, + outputs=outputs, algorithm_spec=algo_spec, - user_vpc_config=self.user_vpc_config.to_dict() - if self.user_vpc_config - else None, - experiment_config=self.experiment_config.to_dict() - if self.experiment_config - else None, + user_vpc_config=( + self.user_vpc_config.model_dump() if self.user_vpc_config else None + ), + experiment_config=( + self.experiment_config.model_dump() if self.experiment_config else None + ), labels=self.labels, + wait=wait, + show_logs=show_logs, ) - job = _Job.get(job_id) - print(f"View the job {job_id} by accessing the console URI: {job.console_uri}") - return job - def wait(self, interval: int = 2, show_logs: bool = True, all_jobs: bool = False): - """Block until the jobs is completed. - - Args: - interval(int): Interval to reload job status - show_logs(bool): Specifies whether to fetch and print the logs produced by - the job. - all_jobs(bool): Wait latest job or wait all jobs in processor, show_logs disabled while - wait all jobs. + def _gen_job_display_name(self, job_name=None): + """Generate job display name.""" + if job_name: + return job_name + ts = datetime.now().strftime("%Y%m%d_%H%M%S") + return "{}_{}".format(self.base_job_name or "processing_job", ts) - Raises: - RuntimeError: If no job is submitted. + def _build_algorithm_spec(self, code_input: CodeDir) -> AlgorithmSpec: + """Build a temporary AlgorithmSpec used for submitting the Job.""" - """ - if all_jobs: - if not self._jobs: - raise RuntimeError("Could not find any submitted job.") - - remains = set(self._jobs) - while remains: - for job in self._jobs: - if job in remains and job.is_completed(): - remains.remove(job) - - time.sleep(interval) - - self._generate_jobs_report() - else: - if not self._latest_job: - raise RuntimeError("Could not find a submitted job.") - - self._latest_job.wait(interval=interval, show_logs=show_logs) - - def _generate_jobs_report(self): - """Generate current jobs report and output to stdout""" - print(f"Jobs status report, total jobs count: {len(self._jobs)}") - - rows = [] - headers = ["JobName", "JobID", "Status"] - for job in self._jobs: - rows.append([job.training_job_name, job.id, job.status]) - - column_widths = [ - max(len(str(value)) for value in column) for column in zip(headers, *rows) - ] - header_row = " | ".join( - f"{header:<{column_widths[i]}}" for i, header in enumerate(headers) + algorithm_spec = AlgorithmSpec( + command=( + self.command + if isinstance(self.command, list) + else [ + "sh", + "-c", + self.command, + ] + ), + image=self.image_uri, + job_type=self.job_type, + code_dir=code_input, + input_channels=self._input_channels or [], + output_channels=self._output_channels or [], ) + return algorithm_spec - print(header_row) - print("-" * len(header_row)) - for row in rows: - print( - " | ".join( - f"{str(value):<{column_widths[i]}}" for i, value in enumerate(row) - ) - ) - - def _get_job_base_output_path(self, job_name: str) -> str: + def _training_job_base_output(self, job_name: str) -> str: """Generate the base output path for the job.""" bucket_name = self.session.oss_bucket.bucket_name @@ -467,105 +297,22 @@ def _get_job_base_output_path(self, job_name: str) -> str: ) return f"oss://{bucket_name}/{job_output_path}" - def _build_input_data_configs( - self, - inputs: Dict[str, Any] = None, - ) -> List[Dict[str, str]]: - """Build the input data config for jobs.""" - - res = [] - remain_inputs = {} - - if self._input_channel_definitions: - remains = set(inputs.keys()) - for channel in self._input_channel_definitions: - channel_name = channel["Name"] - channel_required = channel["Required"] - channel_config = {"Name": channel_name} - - if channel_name in inputs: - updated_value = get_input_channel_config(inputs[channel_name]) - channel_config.update(updated_value) - res.append(channel_config) - remains.remove(channel_name) - elif channel_required: - raise ValueError( - f"Input channel {channel_name} is required but not provided." - " Please check the input channels definition." - ) - - # follow the rest of user input channels in Processor. - remain_inputs = {channel: inputs[channel] for channel in remains} - - if remains: - logger.warning( - f"Following input channels={list(remains)} are not defined in input" - " channels definition. Please check the input channels definition." - ) - - for name, item in remain_inputs.items(): - channel_config = {"Name": name} - updated_value = get_input_channel_config(item) - channel_config.update(updated_value) - res.append(channel_config) - - return res - - def _build_output_data_configs( - self, - output_path: str, - outputs: Dict[str, Any] = None, - ) -> List[Dict[str, str]]: - """Build the output data config for jobs.""" - - res = [] - - if self._output_channel_definitions: - # we create all channel config no matter whether channel is required or not for backward compatibility. - for channel in self._output_channel_definitions: - channel_name = channel["Name"] - channel_config = {"Name": channel_name} - - if channel_name in outputs: - updated_value = get_output_channel_config(outputs[channel_name]) - channel_config.update(updated_value) - else: - output_uri = posixpath.join(output_path, channel["Name"]) - updated_value = get_output_channel_config(output_uri) - channel_config.update(updated_value) - - res.append(channel_config) - else: - for name, item in outputs.items(): - channel_config = {"Name": name} - updated_value = get_output_channel_config(item) - channel_config.update(updated_value) - - res.append(channel_config) - - return res - - @property - def latest_job(self): - """Return the latest submitted processing job.""" - return self._latest_job - def get_outputs_data(self) -> Dict[str, str]: """Show all outputs data paths. Returns: dict[str, str]: A dictionary of all outputs data paths. """ - if not self._latest_job: + if not self.latest_job: raise RuntimeError("Current no Job for the processor.") return { ch["Name"]: ch["OutputUri"] or ch["DatasetId"] - for ch in self._latest_job.output_channels + for ch in self.latest_job.output_channels } - def set_input_channel_definitions(self, definitions: List[Dict[str, Any]]): - self._input_channel_definitions = definitions + def set_input_channels(self, channels: List[Channel]): + self._input_channels = channels - def set_output_channel_definitions(self, definitions: List[Dict[str, Any]]): - self._output_channel_definitions = definitions + def set_output_channels(self, channels: List[Channel]): + self._output_channels = channels diff --git a/pai/schema/__init__.py b/pai/schema/__init__.py deleted file mode 100644 index b42e873..0000000 --- a/pai/schema/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright 2023 Alibaba, Inc. or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/pai/schema/base.py b/pai/schema/base.py deleted file mode 100644 index eb81c37..0000000 --- a/pai/schema/base.py +++ /dev/null @@ -1,107 +0,0 @@ -# Copyright 2023 Alibaba, Inc. or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from marshmallow import EXCLUDE, Schema, fields, post_dump, pre_load - -from ..common.utils import camel_to_snake, snake_to_camel - - -class EntitySchema(Schema): - def __init__(self, instance=None, **kwargs): - super(EntitySchema, self).__init__(**kwargs) - self.instance = instance - - -class BaseAPIResourceSchema(Schema): - """Base schema using in API object serialization and deserialization.""" - - class Meta(object): - unknown = EXCLUDE - - _DefaultFieldsNameMapping = { - "GmtCreateTime": "create_time", - "GmtModifiedTime": "modified_time", - } - - # Mapping API object field name to Python Object/Schema field name.. - FieldNameMapping = {} - - def __init__(self, instance=None, session=None, **kwargs): - super(BaseAPIResourceSchema, self).__init__(**kwargs) - self.instance = instance - self.session = session - - @pre_load - def _filed_name_load_preprocess(self, data, **kwargs): - """Input API object preprocess. - - Transform the input data key to entity filed name. - """ - result = dict() - for name, value in data.items(): - if name in self.FieldNameMapping: - result[self.FieldNameMapping[name]] = value - else: - result[camel_to_snake(name)] = value - return result - - @post_dump - def _filed_name_dump_postprocess(self, data, **kwargs): - """Transform output field name to camel case.""" - filed_name_mapping = self._DefaultFieldsNameMapping.copy() - filed_name_mapping.update(self.FieldNameMapping) - - field_mapping_rev = {value: key for key, value in filed_name_mapping.items()} - result = dict() - for key, value in data.items(): - if value is None: - continue - if key in field_mapping_rev: - result[field_mapping_rev[key]] = value - else: - result[snake_to_camel(key)] = value - return result - - def make_or_reload(self, instance_cls, data): - """Make an instance or reload the instance.""" - if self.instance: - self.instance.__init__(**data) - return self.instance - else: - return instance_cls(session=self.session, **data) - - -class ListOfKVField(fields.Field): - """Mapping a List of key, value to a Dict.""" - - def _serialize(self, value, attr, obj, **kwargs): - res = [] - if not value: - return res - for k, v in value.items(): - res.append( - { - "Key": k, - "Value": v, - } - ) - return res - - def _deserialize(self, value, attr, data, **kwargs): - res = dict() - if not value: - return res - for item in value: - res[item["Key"]] = item["Value"] - return res diff --git a/pai/schema/training_job_schema.py b/pai/schema/training_job_schema.py deleted file mode 100644 index 27b99f9..0000000 --- a/pai/schema/training_job_schema.py +++ /dev/null @@ -1,113 +0,0 @@ -# Copyright 2023 Alibaba, Inc. or its affiliates. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from marshmallow import fields, post_load - -from .base import BaseAPIResourceSchema, ListOfKVField - - -class HyperparameterField(fields.Field): - """Convert between hyperparameters in Dict to hyperparameters in API Object.""" - - def _serialize(self, value, attr, obj, **kwargs): - res = [] - if not value: - return res - for k, v in value.items(): - res.append( - { - "Name": k, - "Value": v, - } - ) - return res - - def _deserialize(self, value, attr, data, **kwargs): - res = dict() - if not value: - return res - for item in value: - res[item["Name"]] = item["Value"] - return res - - -class TrainingJobMetricSchema(BaseAPIResourceSchema): - name = fields.Str() - timestamp = fields.Str() - value = fields.Float() - - -class TrainingJobSchedulerSchema(BaseAPIResourceSchema): - max_running_time_in_seconds = fields.Int() - - -class TrainingJobChannelSchema(BaseAPIResourceSchema): - dataset_id = fields.Str() - input_uri = fields.Str() - name = fields.Str() - - -class TrainingJobStatusTransitionSchema(BaseAPIResourceSchema): - end_time = fields.Str() - reason_code = fields.Str() - reason_message = fields.Str() - start_time = fields.DateTime() - status = fields.Str() - - -class TrainingJobSchema(BaseAPIResourceSchema): - FieldNameMapping = { - "GmtCreateTime": "create_time", - "GmtModifiedTime": "modified_time", - "TrainingJobDescription": "description", - } - - algorithm_name = fields.Str() - algorithm_provider = fields.Str() - algorithm_version = fields.Str() - - hyperparameters = HyperparameterField() - input_channels = fields.List(fields.Dict) - output_channels = fields.List(fields.Dict) - labels = ListOfKVField() - description = fields.Str() - training_job_name = fields.Str() - scheduler = fields.Dict() - compute_resource = fields.Dict() - workspace_id = fields.Str() - experiment_config = fields.Dict() - - # load only fields - latest_metrics = fields.List(fields.Dict) - algorithm_id = fields.Str(load_only=True) - create_time = fields.DateTime(load_only=True) - modified_time = fields.DateTime(load_only=True) - reason_code = fields.Str() - reason_message = fields.Str() - status = fields.Str() - status_transitions = fields.List(fields.Dict) - training_job_id = fields.Str() - training_job_url = fields.Str() - - @post_load - def _make(self, data, **kwargs): - from pai.estimator import _TrainingJob - - data["instance_count"] = data.get("compute_resource", {}).get("EcsCount") - data["instance_type"] = data.get("compute_resource", {}).get("EcsType") - data["max_running_time_in_seconds"] = data.get("scheduler", {}).get( - "MaxRunningTimeInSeconds" - ) - - return self.make_or_reload(_TrainingJob, data) diff --git a/pai/version.py b/pai/version.py index 023d357..7b413b3 100644 --- a/pai/version.py +++ b/pai/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -VERSION = "0.4.7.post0" +VERSION = "0.4.8.dev0" diff --git a/requirements/requirements.txt b/requirements/requirements.txt index f58f634..f5bc171 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -1,13 +1,12 @@ aliyun-python-sdk-core>=2.13.25 alibabacloud_sts20150401 importlib-metadata; python_version < "3.8" -numpy>=1.16.0 +numpy>=1.16.0, <2 oss2>=2.8.0 pyodps>=0.11.0 pyyaml>=5.3.1 six>=1.15.0 -marshmallow -marshmallow-oneofschema>=3.0.1 +pydantic>=2.0.1 eas_prediction>=0.20 alibabacloud_tea_util>=0.3.6, <1.0.0, !=0.3.9 alibabacloud_tea_openapi>=0.3.3, <1.0.0 diff --git a/requirements/test-requirements.txt b/requirements/test-requirements.txt index 6af780b..5693026 100644 --- a/requirements/test-requirements.txt +++ b/requirements/test-requirements.txt @@ -6,3 +6,4 @@ mock>=2.0.0 scikit-learn pandas docker>=4.4.0 +openai diff --git a/setup.py b/setup.py index 43d405d..1dad53c 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ def read_requirements(): setup( name="alipai", - python_requires=">=3.6", + python_requires=">=3.8", version=version, setup_requires=["setuptools_scm"], description="Alibaba Cloud PAI Python SDK", diff --git a/tests/integration/__init__.py b/tests/integration/__init__.py index f33208b..d0f4de7 100644 --- a/tests/integration/__init__.py +++ b/tests/integration/__init__.py @@ -16,9 +16,7 @@ import logging import os -import time import unittest -from functools import wraps import oss2 from odps import ODPS @@ -136,27 +134,6 @@ def setUpClass(cls): if cls.default_session.is_inner: PublicMaxComputeTableDataSet.set_dataset_project("pai_inner_project") cls.odps_client = cls._init_maxc_client() - cls.patch_model_deploy() - - @classmethod - def patch_model_deploy(cls): - """Hack for model deploy wait for service ready.""" - from pai.model import ModelBase - - def deco(f): - @wraps(f) - def _(*args, **kwargs): - wait = kwargs.get("wait") - res = f(*args, **kwargs) - # wait is True which means deploy method should wait until the - # prediction service is 'really' ready. - if wait: - time.sleep(15) - return res - - return _ - - ModelBase.deploy = deco(ModelBase.deploy) @classmethod def tearDownClass(cls): diff --git a/tests/integration/test_estimator.py b/tests/integration/test_estimator.py index ec0ec98..52e266b 100644 --- a/tests/integration/test_estimator.py +++ b/tests/integration/test_estimator.py @@ -13,6 +13,7 @@ # limitations under the License. import os +import posixpath import re from unittest import skipUnless @@ -21,8 +22,9 @@ from pai.common.oss_utils import upload from pai.common.utils import random_str from pai.estimator import AlgorithmEstimator, Estimator -from pai.experiment import Experiment, ExperimentConfig +from pai.experiment import Experiment from pai.image import retrieve +from pai.job._training_job import ExperimentConfig from pai.session import get_default_session from tests.integration import BaseIntegTestCase from tests.integration.utils import t_context @@ -96,6 +98,35 @@ def test_torch_run(self): self.assertIsNotNone(tb.app_uri) tb.delete() + def test_checkpoints(self): + sess = get_default_session() + torch_image_uri = retrieve("pytorch", framework_version="1.12").image_uri + filename = "output.txt" + command = ( + f"echo helloworld > /ml/output/checkpoints/{filename} && echo 'helloworld'" + ) + checkpoint_path = f"oss://{sess.oss_bucket.bucket_name}/sdk-test/test-checkpoints/{random_str(6)}/" + + est = Estimator( + image_uri=torch_image_uri, + command=command, + instance_type="ecs.c6.large", + base_job_name="torch_run_", + checkpoints_path=checkpoint_path, + ) + + est.fit( + inputs={ + "training": self.breast_cancer_train_data_uri, + "test": self.breast_cancer_test_data_uri, + }, + wait=True, + ) + self.assertEqual(checkpoint_path, est.checkpoints_data()) + self.assertTrue( + self.is_oss_object_exists(posixpath.join(checkpoint_path, filename)) + ) + def test_max_compute_input(self): image_uri = retrieve("xgboost", framework_version="latest").image_uri est = Estimator( diff --git a/tests/integration/test_experiment.py b/tests/integration/test_experiment.py index 537f24a..d204513 100644 --- a/tests/integration/test_experiment.py +++ b/tests/integration/test_experiment.py @@ -11,8 +11,8 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -import time +from pai.common.utils import random_str from pai.experiment import Experiment from pai.tensorboard import TensorBoardStatus from tests.integration import BaseIntegTestCase @@ -29,7 +29,7 @@ def setUp(self): def test_create(self): # Init test data - exp_name = "test_experiment_" + str(int(time.time())) + exp_name = "test_experiment_" + random_str(10) # Test create self.experiment = Experiment.create( artifact_uri=self.artifact_uri, @@ -40,7 +40,7 @@ def test_create(self): self.assertEqual(self.experiment.tensorboard_data(), expected_tb_path) def test_update(self): - exp_name = "test_experiment_" + str(int(time.time())) + exp_name = "test_experiment_" + random_str(10) self.experiment = Experiment.create( artifact_uri=self.artifact_uri, name=exp_name, @@ -51,7 +51,7 @@ def test_update(self): self.assertEqual(self.experiment.name, exp_name) def test_list(self): - exp_name = "test_experiment_" + str(int(time.time())) + exp_name = "test_experiment_" + random_str(10) self.experiment = Experiment.create( artifact_uri=self.artifact_uri, name=exp_name, @@ -63,7 +63,7 @@ def test_list(self): self.assertEqual(experiment_names[0], exp_name) def test_get(self): - exp_name = "test_experiment_" + str(int(time.time())) + exp_name = "test_experiment_" + random_str(10) self.experiment = Experiment.create( artifact_uri=self.artifact_uri, name=exp_name, @@ -75,7 +75,7 @@ def test_get(self): self.assertEqual(self.experiment.tensorboard_data(), exp1.tensorboard_data()) def test_tensorboard(self): - exp_name = "test_experiment_" + str(int(time.time())) + exp_name = "test_experiment_" + random_str(10) self.experiment = Experiment.create( artifact_uri=self.artifact_uri, name=exp_name, @@ -84,8 +84,8 @@ def test_tensorboard(self): tb = self.experiment.tensorboard() self.assertIsNotNone(tb.app_uri) self.assertEqual(tb.status, TensorBoardStatus.Running) - tb.stop() + tb.delete() def tearDown(self): - if self.experiment: + if hasattr(self, "experiment") and self.experiment: self.experiment.delete() diff --git a/tests/integration/test_model/__init__.py b/tests/integration/test_model/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/test_model.py b/tests/integration/test_model/test_model.py similarity index 98% rename from tests/integration/test_model.py rename to tests/integration/test_model/test_model.py index e2dac19..8393408 100644 --- a/tests/integration/test_model.py +++ b/tests/integration/test_model/test_model.py @@ -239,8 +239,8 @@ def tearDownClass(cls): def test_tmp_algo_rm_train(self): """Test training registered model with temporary algorithm""" m = RegisteredModel( - model_name="easynlp_pai_bert_tiny_zh", - model_version="0.1.0", + model_name="qwen1.5-0.5b-chat", + # model_version="0.1.0", model_provider="pai", ) @@ -284,6 +284,8 @@ def test_builtin_algo_rm_train(self): inputs = m.get_estimator_inputs() est.hyperparameters["max_epochs"] = 5 est.hyperparameters["warmup_epochs"] = 2 + est.hyperparameters["image_scale"] = "640,640" + est.hyperparameters["train_batch_size"] = 8 est.fit(inputs=inputs) outputs_data = est.get_outputs_data() diff --git a/tests/integration/test_processor.py b/tests/integration/test_processor.py index 98e5ab5..22057a9 100644 --- a/tests/integration/test_processor.py +++ b/tests/integration/test_processor.py @@ -15,8 +15,9 @@ import os from pai.common.utils import random_str -from pai.experiment import Experiment, ExperimentConfig +from pai.experiment import Experiment from pai.image import retrieve +from pai.job import ExperimentConfig from pai.processor import Processor from pai.session import get_default_session from tests.integration import BaseIntegTestCase diff --git a/tests/integration/tests_pipeline/test_pipeline_build.py b/tests/integration/tests_pipeline/test_pipeline_build.py index 3079f6c..f371cc1 100644 --- a/tests/integration/tests_pipeline/test_pipeline_build.py +++ b/tests/integration/tests_pipeline/test_pipeline_build.py @@ -135,7 +135,7 @@ def test_conflict_step_names(self): }, ) - with self.assertRaisesRegexp(ValueError, "name conflict") as _: + with self.assertRaisesRegex(ValueError, "name conflict") as _: _ = Pipeline(steps=[split_step_1, split_step_2]) def test_auto_step_name(self): @@ -207,7 +207,7 @@ def test_pipeline_cycle_detect(self): ) data_source_step.after(type_transform_step) - with self.assertRaisesRegexp(ValueError, "Cycle dependency detected") as _: + with self.assertRaisesRegex(ValueError, "Cycle dependency detected") as _: _ = Pipeline( steps=[type_transform_step, data_source_step], ) diff --git a/tests/integration/utils.py b/tests/integration/utils.py index dbca156..bf6a3c0 100644 --- a/tests/integration/utils.py +++ b/tests/integration/utils.py @@ -17,6 +17,7 @@ import io import os import shutil +import subprocess import uuid from collections import namedtuple @@ -85,7 +86,14 @@ def __init__(self, pai_service_config, oss_config, maxc_config): @property def has_docker(self): - return shutil.which("docker") is not None + # Check if docker daemon is running + return ( + shutil.which("docker") is not None + and subprocess.run( + ["docker", "stats"], stdout=subprocess.PIPE, stderr=subprocess.PIPE + ).returncode + == 0 + ) @property def has_gpu(self): @@ -170,9 +178,11 @@ def make_resource_name(case_name, resource_type=None, sep="-", time_suffix=True) "sdktest", resource_type, case_name, - datetime.datetime.now().isoformat(timespec="seconds") - if time_suffix - else random_str(10), + ( + datetime.datetime.now().isoformat(timespec="seconds") + if time_suffix + else random_str(10) + ), ], ) ) diff --git a/tests/test_data/read_mc_table/run.py b/tests/test_data/read_mc_table/run.py index faf0a59..915916d 100644 --- a/tests/test_data/read_mc_table/run.py +++ b/tests/test_data/read_mc_table/run.py @@ -1,5 +1,6 @@ import json import os +from itertools import islice from odps import ODPS from odps.accounts import StsAccount @@ -40,7 +41,7 @@ def read_table(): o = ODPS(account=account, project=project_name, endpoint=endpoint) # 读取输入表数据 - for record in o.read_table(table_name): + for record in islice(o.read_table(table_name), 20): print(record) diff --git a/tests/unit/test_pipeline/test_pipeline.py b/tests/unit/test_pipeline/test_pipeline.py index a09fd6a..c16df4d 100644 --- a/tests/unit/test_pipeline/test_pipeline.py +++ b/tests/unit/test_pipeline/test_pipeline.py @@ -50,7 +50,7 @@ def test_io_name_conflict(self): }, ) - with self.assertRaisesRegexp(ValueError, ".*conflict.*") as _: + with self.assertRaisesRegex(ValueError, ".*conflict.*") as _: step1 = op.as_step(name="step1") step2 = op.as_step(name="step2") _ = Pipeline( From fb932ab2908f0509b0413b74c74a1dcbcbe1bfaf Mon Sep 17 00:00:00 2001 From: LiangQuan Date: Fri, 28 Jun 2024 15:55:05 +0800 Subject: [PATCH 32/59] feat: add ModelTrainingRecipe/ModelRecipe (#29) * Add ModelTrainingRecipe/ModelRecipe * Deprecate model.get_estimator/get_eval_processor --- pai/huggingface/model.py | 2 +- pai/job/__init__.py | 4 +- pai/job/_training_job.py | 43 +- pai/model/__init__.py | 36 + pai/{model.py => model/_model.py} | 163 +++- pai/model/_model_recipe.py | 720 ++++++++++++++++++ pai/modelscope/model.py | 2 +- pai/predictor.py | 2 +- tests/integration/test_model/test_model.py | 3 +- .../test_model/test_model_recipe.py | 91 +++ tests/integration/test_processor.py | 4 +- .../chinese_medical/train_sampled.json | 202 +++++ 12 files changed, 1201 insertions(+), 71 deletions(-) create mode 100644 pai/model/__init__.py rename pai/{model.py => model/_model.py} (94%) create mode 100644 pai/model/_model_recipe.py create mode 100644 tests/integration/test_model/test_model_recipe.py create mode 100644 tests/test_data/chinese_medical/train_sampled.json diff --git a/pai/huggingface/model.py b/pai/huggingface/model.py index 63be221..455dd26 100644 --- a/pai/huggingface/model.py +++ b/pai/huggingface/model.py @@ -17,7 +17,7 @@ from ..common.logging import get_logger from ..common.utils import to_semantic_version from ..image import ImageLabel -from ..model import ( +from ..model._model import ( DefaultServiceConfig, ModelBase, ResourceConfig, diff --git a/pai/job/__init__.py b/pai/job/__init__.py index 85ae444..6d2769c 100644 --- a/pai/job/__init__.py +++ b/pai/job/__init__.py @@ -20,7 +20,7 @@ ExperimentConfig, HyperParameterDefinition, InstanceSpec, - ModelTrainingSpec, + ModelRecipeSpec, OssLocation, TrainingJob, TrainingJobStatus, @@ -32,7 +32,7 @@ __all__ = [ "TrainingJob", - "ModelTrainingSpec", + "ModelRecipeSpec", "TrainingJobStatus", "Channel", "HyperParameterDefinition", diff --git a/pai/job/_training_job.py b/pai/job/_training_job.py index ea2508e..8cfc57a 100644 --- a/pai/job/_training_job.py +++ b/pai/job/_training_job.py @@ -282,7 +282,7 @@ class AlgorithmSpec(BaseAPIModel): code_dir: Optional[CodeDir] = Field(None, description="Source code location.") -class ModelTrainingSpec(BaseAPIModel): +class ModelRecipeSpec(BaseAPIModel): compute_resource: Optional[ComputeResource] = None hyperparameters: List[HyperParameter] = Field( default_factory=list, alias="HyperParameters" @@ -300,9 +300,6 @@ class ModelTrainingSpec(BaseAPIModel): requirements: Optional[List[str]] = None -ModelEvaluationSpec = ModelTrainingSpec - - class TrainingJob(BaseAPIModel): """TrainingJob represents a training job in the PAI service.""" @@ -616,16 +613,17 @@ def build_inputs( self, inputs: Dict[str, Any], input_channels: List[Channel], - default_inputs: List[Union[DatasetConfig, UriInput]] = None, + default_inputs: Optional[Dict[str, Any]] = None, ) -> List[Dict[str, str]]: res = [] inputs = inputs or dict() input_channels = input_channels or [] - default_inputs = default_inputs or [] - - input_keys = set(list(inputs.keys()) + [item.name for item in default_inputs]) + default_inputs = default_inputs or {} - requires = {ch.name for ch in input_channels if ch.required} - input_keys + inputs = {**default_inputs, **inputs} + requires = {ch.name for ch in input_channels if ch.required} - set( + inputs.keys() + ) if requires: raise ValueError( "Required input channels are not provided: {}".format( @@ -636,9 +634,6 @@ def build_inputs( input_config = self._get_input_config(name, item) res.append(input_config.model_dump()) - for item in default_inputs: - res.append(item.model_dump()) - return res @staticmethod @@ -768,7 +763,7 @@ def _submit( @classmethod def _get_input_config( cls, name: str, item: Union[str, "FileSystemInputBase", DatasetConfig] - ): + ) -> Union[UriInput, DatasetConfig]: """Get input uri for training_job from given input.""" from pai.estimator import FileSystemInputBase @@ -790,20 +785,18 @@ def _get_input_config( name=name, input_uri=item, ) - elif os.path.exists(item): - store_path = Session.get_storage_path_by_category( - StoragePathCategory.InputData - ) - input_ = UriInput(name=name, input_uri=upload(item, store_path)) - elif is_dataset_id(item): - input_ = DatasetConfig( - dataset_id=item, - name=name, - ) + elif isinstance(item, str): + if os.path.exists(item): + store_path = Session.get_storage_path_by_category( + StoragePathCategory.InputData + ) + input_ = UriInput(name=name, input_uri=upload(item, store_path)) + else: + raise ValueError("Invalid input data path, file not found: {item}.") else: raise ValueError( - "Invalid input data, supported inputs are OSS, NAS, MaxCompute " - "table or local path." + f"Invalid input data, supported inputs are OSS, NAS, MaxCompute " + f"table or local path: {type(item)}." ) return input_ diff --git a/pai/model/__init__.py b/pai/model/__init__.py new file mode 100644 index 0000000..7f52e57 --- /dev/null +++ b/pai/model/__init__.py @@ -0,0 +1,36 @@ +# Copyright 2024 Alibaba, Inc. or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +from ._model import ( + InferenceSpec, + Model, + ModelFormat, + RegisteredModel, + ResourceConfig, + container_serving_spec, +) +from ._model_recipe import ModelRecipe, ModelRecipeType, ModelTrainingRecipe + +__all__ = [ + "RegisteredModel", + "Model", + "ModelFormat", + "InferenceSpec", + "ResourceConfig", + "container_serving_spec", + "ModelTrainingRecipe", + "ModelRecipe", + "ModelRecipeType", +] diff --git a/pai/model.py b/pai/model/_model.py similarity index 94% rename from pai/model.py rename to pai/model/_model.py index 52c9a7a..43ab038 100644 --- a/pai/model.py +++ b/pai/model/_model.py @@ -23,32 +23,34 @@ import textwrap import time import typing +import warnings from typing import Any, Dict, Iterator, List, Optional, Tuple, Union import requests from addict import Dict as AttrDict from oss2 import ObjectIterator -from .common import ProviderAlibabaPAI, git_utils -from .common.consts import INSTANCE_TYPE_LOCAL_GPU, ModelFormat, StoragePathCategory -from .common.docker_utils import ContainerRun, run_container -from .common.logging import get_logger -from .common.oss_utils import OssUriObj, download, is_oss_uri, upload -from .common.utils import ( +from ..common import ProviderAlibabaPAI, git_utils +from ..common.consts import INSTANCE_TYPE_LOCAL_GPU, ModelFormat, StoragePathCategory +from ..common.docker_utils import ContainerRun, run_container +from ..common.logging import get_logger +from ..common.oss_utils import OssUriObj, download, is_oss_uri, upload +from ..common.utils import ( generate_repr, is_local_run_instance_type, random_str, to_plain_text, ) -from .exception import DuplicatedMountException -from .image import ImageInfo -from .job import InstanceSpec, ModelTrainingSpec, UriInput, UserVpcConfig -from .predictor import AsyncPredictor, LocalPredictor, Predictor, ServiceType -from .serializers import SerializerBase -from .session import Session, get_default_session +from ..exception import DuplicatedMountException +from ..image import ImageInfo +from ..job._training_job import InstanceSpec, ModelRecipeSpec, UriInput, UserVpcConfig +from ..predictor import AsyncPredictor, LocalPredictor, Predictor, ServiceType +from ..serializers import SerializerBase +from ..session import Session, get_default_session if typing.TYPE_CHECKING: - from pai.estimator import AlgorithmEstimator + from ..estimator import AlgorithmEstimator + from ._model_recipe import ModelRecipe, ModelRecipeType, ModelTrainingRecipe logger = get_logger(__name__) @@ -1465,6 +1467,7 @@ def __init__( self.model_version = self._model_version_info.get("VersionName") self.training_spec = self._model_version_info.get("TrainingSpec") self.evaluation_spec = self._model_version_info.get("EvaluationSpec") + self.compression_spec = self._model_version_info.get("CompressionSpec") self.model_labels = { lb["Key"]: lb["Value"] for lb in self._model_info.get("Labels", []) } @@ -1840,35 +1843,52 @@ def _build_service_config( return inference_spec.to_dict() - def get_training_spec(self, training_method: Optional[str]) -> ModelTrainingSpec: - if type(self)._is_multiple_spec(self.training_spec): - supported_training_methods = list(self.training_spec.keys()) - if training_method and training_method not in supported_training_methods: + def get_recipe_spec( + self, recipe_type: "ModelRecipeType", method: Optional[str] = None + ) -> ModelRecipeSpec: + from ._model_recipe import ModelRecipeType + + if recipe_type == ModelRecipeType.TRAINING: + raw_spec = self.training_spec + elif recipe_type == ModelRecipeType.EVALUATION: + raw_spec = self.evaluation_spec + elif recipe_type == ModelRecipeType.COMPRESSION: + raw_spec = self.compression_spec + else: + raise ValueError( + f"Invalid recipe_type: {recipe_type}. Supported recipe types are:" + f" {ModelRecipeType.supported_types()}" + ) + + if type(self)._is_multiple_spec(raw_spec): + supported_methods = list(raw_spec.keys()) + if method and method not in supported_methods: raise ValueError( - "The model does not support the given training method:" - f" {training_method}. Supported training methods are:" - f" {supported_training_methods}." + "The model recipe does not support the given method:" + f" {method}. Supported methods are: {supported_methods}." ) - elif training_method: - ts = self.training_spec.get(training_method) + elif method: + spec = raw_spec.get(method) else: - training_method = supported_training_methods[0] + method = supported_methods[0] logger.warning( - "The training method is not specified, using the default training" - " method: %s. Supported training methods are: %s.", - training_method, - supported_training_methods, + f"Model recipe contains multiple specs and method is not specified. " + f"Default method is used: '{method}'. Supported training methods are:" + f" {supported_methods}." ) - ts = self.training_spec.get(training_method) + spec = raw_spec.get(method) else: - # Does not support training methods. # Use default training spec. - if training_method: + if method: raise ValueError( - "The model does not support choosing training method. Do not" - " specify the training method." + "The model recipe contains only one spec, do not specify the method." ) - ts = self.training_spec - return ModelTrainingSpec.model_validate(ts) + spec = raw_spec + return ModelRecipeSpec.model_validate(spec) + + def get_training_spec(self, training_method: Optional[str]) -> ModelRecipeSpec: + from ._model_recipe import ModelRecipeType + + return self.get_recipe_spec(ModelRecipeType.TRAINING, training_method) def get_estimator( self, @@ -1913,7 +1933,13 @@ def get_estimator( Returns: :class:`pai.estimator.AlgorithmEstimator`: An AlgorithmEstimator object. """ - from .estimator import AlgorithmEstimator + from ..estimator import AlgorithmEstimator + + warnings.warn( + "`.get_estimator` is deprecated and will be removed in a future version, you can now use " + "`.training_recipe` instead.", + category=FutureWarning, + ) if not self.training_spec: raise ValueError( @@ -2010,6 +2036,13 @@ def get_estimator_inputs(self, training_method=None) -> Dict[str, Any]: Returns: Dict[str, str]: A dict of input channels. """ + + warnings.warn( + "`.get_estimator_inputs` is deprecated and will be removed in a future version, you can now use " + "`.training_recipe().default_inputs` instead.", + category=FutureWarning, + ) + default_inputs = ( self.get_training_spec(training_method=training_method).inputs or [] ) @@ -2067,14 +2100,20 @@ def get_eval_processor( Returns: :class:`pai.processor.Processor`: An Processor object. """ - from .processor import Processor + from ..processor import Processor + + warnings.warn( + "`.get_eval_processor` is deprecated and will be removed in a future version, you can now use " + "`.model_recipe` instead.", + category=FutureWarning, + ) eval_spec = self._get_evaluation_spec() if not eval_spec: raise ValueError( "The provided registered model does not contain evaluation spec." ) - eval_spec = ModelTrainingSpec.model_validate(eval_spec) + eval_spec = ModelRecipeSpec.model_validate(eval_spec) if not eval_spec.algorithm_spec: raise ValueError( "Invalid evaluation spec, the evaluation spec does not contain any" @@ -2136,11 +2175,17 @@ def get_evaluation_inputs(self) -> Dict[str, Any]: Returns: dict[str, str]: A dict of input channels. """ + warnings.warn( + "`.get_eval_inputs` is deprecated and will be removed in a future version, you can now use " + "`.model_recipe().default_inputs` instead.", + category=FutureWarning, + ) + if not self.evaluation_spec: raise ValueError( "The provided registered model does not contain evaluation spec." ) - eval_spec = ModelTrainingSpec.model_validate(self.evaluation_spec) + eval_spec = ModelRecipeSpec.model_validate(self.evaluation_spec) inputs = eval_spec.inputs or [] res = {} @@ -2155,3 +2200,45 @@ def _is_multiple_spec(cls, spec: Dict[str, Any]) -> bool: def _get_evaluation_spec(self): """Get the evaluation_spec of the registered model.""" return self.evaluation_spec + + def training_recipe(self, method: Optional[str] = None) -> "ModelTrainingRecipe": + """Get the training recipe of the registered model. + + Args: + method (str, optional): The training method used to select the + specific training recipe. + + Returns: + :class:`pai.model.ModelTrainingRecipe`: A ModelTrainingRecipe object. + + """ + from ._model_recipe import ModelTrainingRecipe + + return ModelTrainingRecipe( + model_name=self.model_name, + model_version=self.model_version, + model_provider=self.model_provider, + method=method, + ) + + def model_recipe( + self, recipe_type: "ModelRecipeType", method: Optional[str] = None + ) -> "ModelRecipe": + """Initialize a ModelRecipe object from the recipe spec of the registered model. + + Args: + recipe_type (ModelRecipeType): The recipe type used to select the specific model recipe. + supported recipe types are: "training", "evaluation", "compression". + method (str, optional): The method used to select the specific model recipe. + + Returns: + :class:`pai.model.ModelRecipe`: A ModelRecipe object. + + """ + return ModelRecipe( + model_name=self.model_name, + model_version=self.model_version, + model_provider=self.model_provider, + recipe_type=recipe_type, + method=method, + ) diff --git a/pai/model/_model_recipe.py b/pai/model/_model_recipe.py new file mode 100644 index 0000000..39f35fc --- /dev/null +++ b/pai/model/_model_recipe.py @@ -0,0 +1,720 @@ +# Copyright 2023 Alibaba, Inc. or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import dataclasses +import enum +import shutil +from typing import Any, Dict, List, Optional, Tuple, Union + +from ..common.logging import get_logger +from ..common.oss_utils import download, is_oss_uri +from ..job._training_job import ( + DEFAULT_OUTPUT_MODEL_CHANNEL_NAME, + AlgorithmSpec, + Channel, + ComputeResource, + DatasetConfig, + ExperimentConfig, + InstanceSpec, + ModelRecipeSpec, + OssLocation, + TrainingJob, + UriInput, + UserVpcConfig, + _TrainingJobSubmitter, +) +from ..predictor import Predictor +from ..session import get_default_session +from ._model import InferenceSpec, Model, RegisteredModel, ResourceConfig + +logger = get_logger(__name__) + + +@dataclasses.dataclass +class RecipeInitKwargs(object): + model_name: Optional[str] + model_version: Optional[str] + model_provider: Optional[str] + method: Optional[str] + # following fields are generated from model or overridden + model_channel_name: Optional[str] + model_uri: Optional[str] + hyperparameters: Optional[Dict[str, Any]] + job_type: Optional[str] + image_uri: Optional[str] + source_dir: Optional[str] + command: Union[str, List[str]] + resource_id: Optional[str] + instance_count: Optional[int] + instance_type: Optional[str] + instance_spec: Optional[InstanceSpec] + max_run_time: Optional[int] + labels: Optional[Dict[str, str]] + requirements: Optional[List[str]] + environments: Optional[Dict[str, str]] + input_channels: Optional[List[Channel]] + output_channels: Optional[List[Channel]] + default_inputs: Optional[Union[UriInput, DatasetConfig]] + + +class ModelRecipeType(enum.Enum): + TRAINING = "training" + EVALUATION = "evaluation" + COMPRESSION = "compression" + + @classmethod + def supported_types(cls): + return [cls.TRAINING, cls.EVALUATION, cls.COMPRESSION] + + +class ModelRecipe(_TrainingJobSubmitter): + MODEL_CHANNEL_NAME = "model" + + def __init__( + self, + model_name: Optional[str] = None, + model_version: Optional[str] = None, + model_provider: Optional[str] = None, + model_uri: Optional[str] = None, + recipe_type: ModelRecipeType = ModelRecipeType.TRAINING, + method: Optional[str] = None, + source_dir: Optional[str] = None, + model_channel_name: Optional[str] = "model", + hyperparameters: Optional[Dict[str, Any]] = None, + job_type: Optional[str] = None, + image_uri: Optional[str] = None, + command: Union[str, List[str]] = None, + instance_count: Optional[int] = None, + instance_type: Optional[str] = None, + instance_spec: Optional[InstanceSpec] = None, + resource_id: Optional[str] = None, + user_vpc_config: Optional[UserVpcConfig] = None, + labels: Optional[Dict[str, str]] = None, + requirements: Optional[List[str]] = None, + environments: Optional[Dict[str, str]] = None, + experiment_config: Optional[ExperimentConfig] = None, + input_channels: Optional[List[Channel]] = None, + output_channels: Optional[List[Channel]] = None, + max_run_time: Optional[int] = None, + default_inputs: Optional[Dict[str, Any]] = None, + base_job_name: Optional[str] = None, + ): + init_kwargs = self._init_kwargs( + model_name=model_name, + model_version=model_version, + model_provider=model_provider, + recipe_type=recipe_type, + method=method, + # get from model or override + model_uri=model_uri, + model_channel_name=model_channel_name, + hyperparameters=hyperparameters, + job_type=job_type, + image_uri=image_uri, + source_dir=source_dir, + command=command, + instance_count=instance_count, + instance_spec=instance_spec, + instance_type=instance_type, + labels=labels, + requirements=requirements, + environments=environments, + input_channels=input_channels, + output_channels=output_channels, + default_inputs=default_inputs, + max_run_time=max_run_time, + ) + self.model_name = init_kwargs.model_name + self.model_version = init_kwargs.model_version + self.model_provider = init_kwargs.model_provider + self.method = init_kwargs.method + self.model_uri = init_kwargs.model_uri + self.model_channel_name = init_kwargs.model_channel_name + self.job_type = init_kwargs.job_type + self.hyperparameters = init_kwargs.hyperparameters + self.image_uri = init_kwargs.image_uri + self.command = init_kwargs.command + self.source_dir = init_kwargs.source_dir + self.default_inputs = init_kwargs.default_inputs + + super().__init__( + base_job_name=base_job_name, + experiment_config=experiment_config, + resource_id=resource_id, + user_vpc_config=user_vpc_config, + instance_type=init_kwargs.instance_type, + instance_count=init_kwargs.instance_count, + instance_spec=init_kwargs.instance_spec, + max_run_time=init_kwargs.max_run_time, + environments=init_kwargs.environments, + requirements=init_kwargs.requirements, + labels=init_kwargs.labels, + ) + + @classmethod + def _init_kwargs( + cls, + model_name: Optional[str] = None, + model_version: Optional[str] = None, + model_provider: Optional[str] = None, + recipe_type: ModelRecipeType = ModelRecipeType.TRAINING, + method: Optional[str] = None, + model_channel_name: Optional[str] = "model", + model_uri: Optional[str] = None, + hyperparameters: Optional[Dict[str, Any]] = None, + job_type: Optional[str] = None, + image_uri: Optional[str] = None, + source_dir: Optional[str] = None, + command: Union[str, List[str]] = None, + instance_count: Optional[int] = None, + instance_type: Optional[str] = None, + resource_id: Optional[str] = None, + instance_spec: Optional[InstanceSpec] = None, + max_run_time: Optional[int] = None, + labels: Optional[Dict[str, str]] = None, + requirements: Optional[List[str]] = None, + environments: Optional[Dict[str, str]] = None, + input_channels: List[Channel] = None, + output_channels: List[Channel] = None, + default_inputs: Optional[Union[UriInput, DatasetConfig]] = None, + ) -> RecipeInitKwargs: + model = ( + RegisteredModel( + model_name=model_name, + model_version=model_version, + model_provider=model_provider, + ) + if model_name + else None + ) + model_recipe_spec = ( + model.get_recipe_spec(recipe_type=recipe_type, method=method) + if model + else None + ) + model_uri = model_uri or (model and model.uri) + if not model_recipe_spec: + return RecipeInitKwargs( + model_name=model_name, + model_version=model_version, + model_provider=model_provider, + method=method, + model_channel_name=model_channel_name, + model_uri=model_uri, + hyperparameters=hyperparameters, + job_type=job_type, + image_uri=image_uri, + source_dir=source_dir, + command=command, + instance_count=instance_count, + instance_type=instance_type, + instance_spec=instance_spec, + resource_id=resource_id, + labels=labels, + requirements=requirements, + environments=environments, + input_channels=input_channels, + output_channels=output_channels, + max_run_time=max_run_time, + default_inputs=default_inputs, + ) + if not model_uri: + input_ = next( + ( + item + for item in model_recipe_spec.inputs + if item.name == model_channel_name + ), + None, + ) + + if input_: + if isinstance(input_, UriInput): + model_uri = input_.input_uri + else: + logger.warning( + "Input channel '%s' is not a URI input: %s", + model_channel_name, + type(input_), + ) + + if not default_inputs and model_recipe_spec.inputs: + default_inputs = {} + for item in model_recipe_spec.inputs: + if isinstance(item, UriInput): + default_inputs[item.name] = item.input_uri + else: + default_inputs[item.name] = item + algorithm_spec = cls._get_algorithm_spec(model_recipe_spec) + if algorithm_spec: + if ( + not source_dir + and algorithm_spec.code_dir + and isinstance(algorithm_spec.code_dir, OssLocation) + ): + source_dir = f"oss://{0}.{1}/{2}".format( + algorithm_spec.code_dir.bucket, + algorithm_spec.code_dir.endpoint, + algorithm_spec.code_dir.key.lstrip("/"), + ) + image_uri = image_uri or algorithm_spec.image + command = command or algorithm_spec.command + job_type = job_type or algorithm_spec.job_type + input_channels = input_channels or algorithm_spec.input_channels + output_channels = output_channels or algorithm_spec.output_channels + + instance_type, instance_spec, instance_count = cls._get_compute_resource_config( + instance_type=instance_type, + instance_spec=instance_spec, + instance_count=instance_count, + resource_id=resource_id, + compute_resource=model_recipe_spec.compute_resource, + ) + hyperparameters = hyperparameters or {} + hyperparameters = { + **{ + hp.name: hp.default_value + for hp in ( + algorithm_spec and algorithm_spec.hyperparameter_definitions or {} + ) + if hp.default_value is not None and hp.default_value != "" + }, + **{hp.name: hp.value for hp in model_recipe_spec.hyperparameters}, + **hyperparameters, + } + requirements = requirements or model_recipe_spec.requirements + environments = environments or model_recipe_spec.environments + return RecipeInitKwargs( + model_name=model_name, + model_version=model_version, + model_provider=model_provider, + method=method, + model_uri=model_uri, + model_channel_name=model_channel_name, + hyperparameters=hyperparameters, + job_type=job_type, + image_uri=image_uri, + source_dir=source_dir, + command=command, + instance_count=instance_count, + instance_spec=instance_spec, + instance_type=instance_type, + max_run_time=max_run_time, + labels=labels, + requirements=requirements, + environments=environments, + input_channels=input_channels, + output_channels=output_channels, + resource_id=resource_id, + default_inputs=default_inputs, + ) + + @staticmethod + def _get_compute_resource_config( + instance_type: str, + instance_count: int, + instance_spec: InstanceSpec, + resource_id: str, + compute_resource: ComputeResource, + ) -> Tuple[str, InstanceSpec, int]: + if resource_id: + if instance_type: + logger.warning( + "The instance type is ignored when resource_id is provided." + ) + instance_spec = instance_spec or ( + compute_resource and compute_resource.instance_spec + ) + if not instance_spec: + raise ValueError( + "Running in dedicated resource group, please provide instance spec" + " for the training job." + ) + instance_count = ( + instance_count + or (compute_resource and compute_resource.instance_count) + or 1 + ) + else: + if instance_spec: + logger.warning( + "The instance spec is ignored when resource_id is not provided." + ) + instance_type = instance_type or ( + compute_resource and compute_resource.ecs_spec + ) + if not instance_type: + raise ValueError("No instance type is specified for the training job") + instance_count = ( + instance_count or (compute_resource and compute_resource.ecs_count) or 1 + ) + return instance_type, instance_spec, instance_count + + @staticmethod + def _get_algorithm_spec(model_recipe_spec: ModelRecipeSpec) -> AlgorithmSpec: + session = get_default_session() + if model_recipe_spec.algorithm_spec: + return model_recipe_spec.algorithm_spec + + if not model_recipe_spec.algorithm_name: + raise ValueError( + "Both algorithm_name and algorithm_spec are not provided " + "in the model training spec." + ) + + algo = session.algorithm_api.get_by_name( + algorithm_name=model_recipe_spec.algorithm_name, + algorithm_provider=model_recipe_spec.algorithm_provider, + ) + raw_algo_version_spec = session.algorithm_api.get_version( + algorithm_id=algo["AlgorithmId"], + algorithm_version=model_recipe_spec.algorithm_version, + ) + return AlgorithmSpec.model_validate(raw_algo_version_spec["AlgorithmSpec"]) + + def _build_algorithm_spec( + self, code_input, inputs: Dict[str, Any] + ) -> AlgorithmSpec: + algorithm_spec = AlgorithmSpec( + command=( + self.command + if isinstance(self.command, list) + else ["sh", "-c", self.command] + ), + image=self.image_uri, + job_type=self.job_type, + code_dir=code_input, + output_channels=self._default_training_output_channels(), + input_channels=[ + Channel(name=channel_name, required=False) + for channel_name in inputs.keys() + ], + ) + return algorithm_spec + + def retrieve_scripts(self, local_path: str) -> str: + """Retrieve the training scripts to the local file system. + + Args: + local_path (str): The local path where the training scripts are saved. + + Returns: + str: The local path where the training scripts are saved. + + """ + + if not self.source_dir: + raise RuntimeError("Source code is not available for the training job.") + + if is_oss_uri(self.source_dir): + return download(self.source_dir, local_path, un_tar=True) + else: + shutil.copytree(self.source_dir, local_path) + return local_path + + def run( + self, + inputs: Optional[Dict[str, Union[str, DatasetConfig]]] = None, + outputs: Optional[Dict[str, Union[str, DatasetConfig]]] = None, + wait: bool = True, + job_name: Optional[str] = None, + show_logs: bool = True, + ) -> TrainingJob: + """Start a training job with the given inputs. + + Args: + inputs (Dict[str, Union[str, DatasetConfig]], optional): A dictionary of inputs + used in the training job. The keys are the channel name and the values are + the URIs of the input data. If not specified, the default inputs will be + used. + wait (bool): Whether to wait for the job to complete before returning. Default + to True. + job_name (str, optional): The name of the training job. If not provided, a default + job name will be generated. + show_logs (bool): Whether to show the logs of the training job. Default to True. + + Returns: + :class:`pai.training.TrainingJob`: A submitted training job. + + """ + job_name = self.job_name(job_name) + + inputs = inputs or dict() + code_input = self._build_code_input(job_name, source_dir=self.source_dir) + algo_spec = self._build_algorithm_spec( + code_input=code_input, + inputs=inputs, + ) + + if self.model_channel_name not in inputs: + inputs[self.model_channel_name] = self.model_uri + + if len(inputs.keys()) == 1 and self.model_channel_name in inputs: + default_inputs = self.default_inputs + else: + default_inputs = None + + inputs = self.build_inputs( + inputs=inputs, + input_channels=algo_spec.input_channels, + default_inputs=default_inputs, + ) + outputs = self.build_outputs( + job_name=job_name, + output_channels=algo_spec.output_channels, + outputs=outputs, + ) + return self._submit( + job_name=job_name, + algorithm_spec=algo_spec, + instance_spec=self.instance_spec, + instance_type=self.instance_type, + instance_count=self.instance_count, + resource_id=self.resource_id, + hyperparameters=self.hyperparameters, + environments=self.environments, + requirements=self.requirements, + max_run_time=self.max_run_time, + inputs=inputs, + outputs=outputs, + user_vpc_config=self.user_vpc_config if self.user_vpc_config else None, + # experiment_config=self.experiment_config if self.experiment_config else None, + labels=self.labels, + wait=wait, + show_logs=show_logs, + ) + + +class ModelTrainingRecipe(ModelRecipe): + """A recipe used to train a model.""" + + def __init__( + self, + model_name: Optional[str] = None, + model_version: Optional[str] = None, + model_provider: Optional[str] = None, + model_uri: Optional[str] = None, + method: Optional[str] = None, + source_dir: Optional[str] = None, + model_channel_name: Optional[str] = "model", + hyperparameters: Optional[Dict[str, Any]] = None, + job_type: Optional[str] = None, + image_uri: Optional[str] = None, + command: Union[str, List[str]] = None, + instance_count: Optional[int] = None, + instance_type: Optional[str] = None, + instance_spec: Optional[InstanceSpec] = None, + resource_id: Optional[str] = None, + user_vpc_config: Optional[UserVpcConfig] = None, + labels: Optional[Dict[str, str]] = None, + requirements: Optional[List[str]] = None, + environments: Optional[Dict[str, str]] = None, + experiment_config: Optional[ExperimentConfig] = None, + input_channels: Optional[List[Channel]] = None, + output_channels: Optional[List[Channel]] = None, + max_run_time: Optional[int] = None, + default_training_inputs: Optional[Dict[str, Any]] = None, + base_job_name: Optional[str] = None, + ): + """Initialize a ModelTrainingRecipe object. + + Args: + model_name (str, optional): The name of the registered model. Default to + None. + model_version (str, optional): The version of the registered model. Default + to None. + model_provider (str, optional): The provider of the registered model. + Optional values are "pai", "huggingface" or None. If None, list + registered models in the workspace of the current session. Default to + None. + method (str, optional): The training method used to select the + specific training recipe while the registered model contains multiple + model training specs. Default to None. + model_channel_name (str, optional): The name of the model channel. Default to + "model". + model_uri (str, optional): The URI of the input pretrained model. If the URI + is not provided, the model from the registered model will be used. + Default to None. + hyperparameters (dict, optional): A dictionary of hyperparameters used in + the training job. Default to None. + job_type (str, optional): The type of the job, supported values are "PyTorch", + "TfJob", "XGBoostJob" etc. + image_uri (str, optional): The URI of the Docker image. Default to None. + source_dir (str, optional): The source code using in the training job, which + is a directory containing the training script or an OSS URI. Default to + None. + command (str or list, optional): The command to execute in the training job. + Default to None. + requirements (list, optional): A list of Python requirements used to install + the dependencies in the training job. Default to None. + instance_count (int, optional): The number of instances to use for training. + Default to None. + instance_type (str, optional): The instance type to use for training. Default + to None. + instance_spec (:class:`pai.model.InstanceSpec`, optional): The resource config + for each instance of the training job. The dedicated resource group must + be provided when the instance spec is set. Default to None. + resource_id (str, optional): The ID of the resource group used to run the + training job. Default to None. + user_vpc_config (:class:`pai.model.UserVpcConfig`, optional): The VPC + configuration used to enable the job instance to connect to the + specified user VPC. Default to None. + environments (dict, optional): A dictionary of environment variables used in + the training job. Default to None. + experiment_config (:class:`pai.model.ExperimentConfig`, optional): The + experiment + labels (dict, optional): A dictionary of labels used to tag the training job. + Default to None. + + """ + super().__init__( + model_name=model_name, + model_version=model_version, + model_provider=model_provider, + model_uri=model_uri, + method=method, + recipe_type=ModelRecipeType.TRAINING, + source_dir=source_dir, + model_channel_name=model_channel_name, + hyperparameters=hyperparameters, + job_type=job_type, + image_uri=image_uri, + command=command, + instance_count=instance_count, + instance_type=instance_type, + instance_spec=instance_spec, + resource_id=resource_id, + user_vpc_config=user_vpc_config, + labels=labels, + requirements=requirements, + environments=environments, + experiment_config=experiment_config, + input_channels=input_channels, + output_channels=output_channels, + max_run_time=max_run_time, + default_inputs=default_training_inputs, + base_job_name=base_job_name, + ) + + def train( + self, + inputs: Optional[Dict[str, Union[str, DatasetConfig]]] = None, + outputs: Optional[Dict[str, Union[str, DatasetConfig]]] = None, + wait: bool = True, + job_name: Optional[str] = None, + show_logs: bool = True, + ) -> TrainingJob: + """Start a training job with the given inputs. + + Args: + inputs (Dict[str, Union[str, DatasetConfig]], optional): A dictionary of inputs + used in the training job. The keys are the channel name and the values are + the URIs of the input data. If not specified, the default inputs will be + used. + outputs (Dict[str, Union[str, DatasetConfig]], optional): A dictionary of outputs + used in the training job. The keys are the channel name and the values are + the URIs or Dataset of the output data. + wait (bool): Whether to wait for the job to complete before returning. Default + to True. + job_name (str, optional): The name of the training job. If not provided, a default + job name will be generated. + show_logs (bool): Whether to show the logs of the training job. Default to True. + + Returns: + :class:`pai.training.TrainingJob`: A submitted training job. + + """ + return self.run( + inputs=inputs, + outputs=outputs, + wait=wait, + job_name=job_name, + show_logs=show_logs, + ) + + def deploy( + self, + service_name: str, + instance_type: Optional[str] = None, + instance_count: int = 1, + resource_config: Optional[Union[ResourceConfig, Dict[str, int]]] = None, + resource_id: str = None, + options: Optional[Dict[str, Any]] = None, + wait=True, + inference_spec: Optional[InferenceSpec] = None, + **kwargs, + ) -> Predictor: + """Deploy the training job output model as a online prediction service. + + Args: + service_name (str): The name of the online prediction service. + instance_type (str, optional): The instance type used to run the service. + instance_count (int, optional): The number of instances used to run the + service. Default to 1. + resource_config (Union[ResourceConfig, Dict[str, int]], optional): The resource + config for the service. Default to None. + resource_id (str, optional): The ID of the resource group used to run the + service. Default to None. + options (Dict[str, Any], optional): The options used to deploy the service. + Default to None. + wait (bool, optional): Whether to wait for the service endpoint to be ready. + inference_spec (:class:`pai.model.InferenceSpec`, optional): The inference + spec used to deploy the service. If not provided, the `inference_spec` of + the model will be used. Default to None. + kwargs: Additional keyword arguments used to deploy the service. + + Returns: + :class:`pai.predictor.Predictor`: A predictor object refers to the created + service. + """ + if not inference_spec and self.model_name: + model = RegisteredModel( + model_name=self.model_name, + model_version=self.model_version, + model_provider=self.model_provider, + ) + inference_spec = model.inference_spec + + if not inference_spec: + raise RuntimeError("No inference_spec is available for model deployment.") + + m = Model( + model_data=self.model_data(), + inference_spec=inference_spec, + ) + p = m.deploy( + service_name=service_name, + instance_type=instance_type, + instance_count=instance_count, + resource_config=resource_config, + resource_id=resource_id, + options=options, + wait=wait, + **kwargs, + ) + return p + + def model_data(self): + + if not self._training_jobs: + raise RuntimeError("No training job is available for deployment.") + + if not self.latest_job.is_succeeded(): + logger.warning( + "The latest training job is not succeeded, the deployment may not work." + ) + + return self.latest_job.output_path( + channel_name=DEFAULT_OUTPUT_MODEL_CHANNEL_NAME + ) diff --git a/pai/modelscope/model.py b/pai/modelscope/model.py index d844fca..971bdb7 100644 --- a/pai/modelscope/model.py +++ b/pai/modelscope/model.py @@ -17,7 +17,7 @@ from ..api.image import ImageLabel from ..common.logging import get_logger from ..common.utils import to_semantic_version -from ..model import ( +from ..model._model import ( DefaultServiceConfig, ModelBase, ResourceConfig, diff --git a/pai/predictor.py b/pai/predictor.py index fced989..7138996 100644 --- a/pai/predictor.py +++ b/pai/predictor.py @@ -205,7 +205,7 @@ def console_uri(self): def _get_default_serializer(self): """Get default serializer for the predictor by inspecting the service config.""" - from pai.model import _BuiltinProcessor + from pai.model._model import _BuiltinProcessor service_config = json.loads(self._service_api_object["ServiceConfig"]) processor_code = service_config.get("processor") diff --git a/tests/integration/test_model/test_model.py b/tests/integration/test_model/test_model.py index 8393408..1d24ca0 100644 --- a/tests/integration/test_model/test_model.py +++ b/tests/integration/test_model/test_model.py @@ -240,7 +240,6 @@ def test_tmp_algo_rm_train(self): """Test training registered model with temporary algorithm""" m = RegisteredModel( model_name="qwen1.5-0.5b-chat", - # model_version="0.1.0", model_provider="pai", ) @@ -251,7 +250,7 @@ def test_tmp_algo_rm_train(self): outputs_data = est.get_outputs_data() self.assertTrue(isinstance(outputs_data, dict)) self.assertTrue(outputs_data) - self.assertTrue(len(outputs_data) == 1) + self.assertTrue(len(outputs_data) == 2) model_path = os.path.join(outputs_data["model"], "model.safetensors") self.assertTrue(self.is_oss_object_exists(model_path)) diff --git a/tests/integration/test_model/test_model_recipe.py b/tests/integration/test_model/test_model_recipe.py new file mode 100644 index 0000000..55d4e6c --- /dev/null +++ b/tests/integration/test_model/test_model_recipe.py @@ -0,0 +1,91 @@ +# Copyright 2024 Alibaba, Inc. or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import os + +import pytest + +from pai.common.utils import camel_to_snake, random_str +from pai.model import RegisteredModel +from tests.integration import BaseIntegTestCase +from tests.test_data import test_data_dir + + +@pytest.mark.timeout(60 * 30) +class TestModelRecipe(BaseIntegTestCase): + + _service_names = [] + + @classmethod + def tearDownClass(cls): + sess = cls.default_session + for s in cls._service_names: + try: + sess.service_api.delete(s) + except Exception as e: + print("Failed to delete service: ", e) + + def _gen_service_name(self, prefix: str = None): + prefix = prefix or "sdk_test_" + camel_to_snake(type(self).__name__) + name = "{}_{}".format(prefix, random_str(6)) + self._service_names.append(name) + return name + + def test_training_e2e(self): + model = RegisteredModel(model_name="qwen1.5-0.5b-chat", model_provider="pai") + training_recipe = model.training_recipe(method="QLoRA_LLM") + training_recipe.train() + self.assertIsNotNone(training_recipe.model_data()) + + predictor = training_recipe.deploy( + service_name=self._gen_service_name("test_recipe_e2e"), + ) + openai = predictor.openai() + resp = openai.chat.completions.create( + model="default", + messages=[ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": "What is the meaning of life?"}, + ], + max_tokens=100, + ) + self.assertIsNotNone(resp.choices[0].message.content) + + def test_custom_inputs_train(self): + model = RegisteredModel(model_name="qwen1.5-0.5b-chat", model_provider="pai") + training_recipe = model.training_recipe(method="QLoRA_LLM") + self.assertTrue( + bool(training_recipe.default_inputs), + "Default inputs is empty for ModelTrainingRecipe.", + ) + + train_data = os.path.join(test_data_dir, "chinese_medical/train_sampled.json") + training_recipe.train( + inputs={ + "train": train_data, + }, + ) + self.assertIsNotNone(training_recipe.model_data()) + predictor = training_recipe.deploy( + service_name=self._gen_service_name("test_custom"), + ) + openai = predictor.openai() + resp = openai.chat.completions.create( + model="default", + messages=[ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": "What is the meaning of life?"}, + ], + max_tokens=100, + ) + self.assertIsNotNone(resp.choices[0].message.content) diff --git a/tests/integration/test_processor.py b/tests/integration/test_processor.py index 22057a9..1878d7c 100644 --- a/tests/integration/test_processor.py +++ b/tests/integration/test_processor.py @@ -77,7 +77,9 @@ def test_train_with_experiment_config(self): command="python main.py --output_path=/ml/output/flag", instance_type="ecs.c6.large", base_job_name="processing", - experiment_config=ExperimentConfig(self.experiment.experiment_id), + experiment_config=ExperimentConfig( + experiment_id=self.experiment.experiment_id + ), ) processor.run( diff --git a/tests/test_data/chinese_medical/train_sampled.json b/tests/test_data/chinese_medical/train_sampled.json new file mode 100644 index 0000000..11c3bf7 --- /dev/null +++ b/tests/test_data/chinese_medical/train_sampled.json @@ -0,0 +1,202 @@ +[ + { + "instruction": "你是一个消化科医生,请根据患者的问题给出建议:肚子疼上不来气老打嗝不排气感觉胸闷堵得上浑身没劲儿", + "output": "根据你的情况来看主要是由於脾胃气滞致使的病症!建议你最好是用些健胃消食,调理脾胃,平时用些小米苡米粥来补益脾胃!其实得了胃胀并不可怕,患者要自己是可以战胜病魔的,只要在早期发觉的时候及时救治,缓解病情及康复的率都非常大。此外还需要建议患者多加注意休息,不能吃辛辣刺激的食物。" + }, + { + "instruction": "你是一个肝病科医生,请根据患者的问题给出建议:如果体液或者血液干了,是不是乙肝病毒也濒死了???", + "output": "根据你的叙述,这种情况不是乙肝病毒不存在了。果有乙肝病毒,建议去医院检查下,并且救治,效果还是很好的。肝病患者可以在理解对症救治的同时,在生活中用食疗做为配置救治,在日常生活饮食中需要有恰当饮食,防止刺激性食物。建议泥去正规专业的医院进行救治。" + }, + { + "instruction": "你是一个肝病科医生,请根据患者的问题给出建议:我有大学同学是挺严重的肝炎,我不知道我的抗体还在不在,在和他相处的过程中应当留意些什么呢?", + "output": "留意点吧,不要和乙型肝炎病人及乙肝病毒携带者共用毛巾、牙膏、被褥等,以免生活接触性传染。其实得了肝炎并不可怕,患者要认为自己是可以战胜病魔的,只要在早期发觉的时候及时救治,纾解病情及康复的几率都非常大。身体如果有不适的地方建议尽快到正规的医院进行检查救治。" + }, + { + "instruction": "你是一个消化科医生,请根据患者的问题给出建议:我有糜烂性胃炎,胃底易发息肉,四肢无力,前些日子吃了中药,也没转好,我得怎么办?", + "output": "胃病需要有慢慢调养,不要着急!建议你平时压制饮食,不要吃辛辣打击食物,多吃温热食物,不要着凉,不定期的复检!胃炎病情很严重,建议患者马上实行救治,期望患者可以根据医生的意见对症救治。同时看重饮食问题,三餐规律,防止辛辣打击食物,以免放柔病情。" + }, + { + "instruction": "你是一个心血管科医生,请根据患者的问题给出建议:我去医院仔细检查说我高血压,这种情况比较近一直都在注意身体方面问题。高血压钾可不可以补充呢?", + "output": "您好,如果患高血压,一般需要有根据病情来口服降压药物,如果同时有高钾血症,就注意不要再专心补氯化钾。如果再次出现了低钾血症,缺钾的情况下是可以补充的,并绝不会影响血压的治疗,一般需要有注意是不是口服的力量药物或者有保钾利尿药物的口服。祝您身体健康。" + }, + { + "instruction": "你是一个肝病科医生,请根据患者的问题给出建议:怀上三个月查出来乙肝小三阳,病毒量24500现在7个半月,要不要采用宫内截断", + "output": "乙肝是一种常见的病毒性传染病,是机体遭到乙肝病毒传染引来的根据你讲述的情况,这个时间不需要做截断救治,宫内传染发生率不低于5%,可以在临产前做截断救治其实得了乙肝并不可怕,患者要认为自己是可以战胜病魔的,只要在早期发觉的时候及时救治,压制病情及康复的几率都非常大。" + }, + { + "instruction": "你是一个肝病科医生,请根据患者的问题给出建议:2001年检查患了乙肝大三阳,有救治过。现在也没好。现在人很瘦,脸色也不好,黄黄的,和这个病友关吗?", + "output": "那要看你的肝功,彩超还正常,如果一直正常,可以暂时不救治,救治大三阳外,建议患者在日常生活中要留意饮食,并且要始终保持愉悦的心情,以此来增强体质放慢康复速度。乙肝大三阳这种疾病不容易治愈,建议去正规专业的医院进行治疗,有不适的地方要尽快到医院进行治疗。" + }, + { + "instruction": "你是一个心血管科医生,请根据患者的问题给出建议:上个月我被查出来有严重高血压,他们叫我注意一些东西,我当时忘了。您好,高血压忌什么?", + "output": "高血压日常饮食建议采用清淡饮食,除了限盐以外饮食没特别的影响,高血压是具有心血管很常见的疾病,需要有长期口服药物治疗。平时需要有适当的活动,可以增进身体的血压平稳,高血压不及时治疗,容易引来中风的情况再次发生。中风就比较麻烦了。平时要注意个人生活习惯,饮食要健康。" + }, + { + "instruction": "你是一个消化科医生,请根据患者的问题给出建议:吃一点儿东西胃就胀;;不好吸收;;而且体重绝不会增强;;", + "output": "有胃肠炎吗?糯米做的东西和硬的食物别吃多活动和吃水果有助消化少吃多餐胃胀这种疾病不易康复。患者朋友理应保持良好的心态,用积极的心态去直面它,只有这样才能降低患者抗衡胃胀的信心,这样一定能获得康复。此外还需要建议患者多加注意休息,不能吃辛辣刺激的食物。" + }, + { + "instruction": "你是一个消化科医生,请根据患者的问题给出建议:胃胀,恶心,打嗝,感觉无法吸收食物。", + "output": "从你叙述的情况看,主要考虑是慢性胃炎的症状。建议最好是去医院做个胃镜看下,主要是要剔除胃癌等特殊情况,胃胀病情严重就要立即对症救治,恰当用药,否则会致使胃胀再次病发,除此之外,患者还需要有恰当饮食,始终保持心情愉快,此外还需要建议患者多加注意休息,不要吃辛辣刺激的食物。" + }, + { + "instruction": "你是一个消化科医生,请根据患者的问题给出建议:我有浅表性胃炎和萎缩性胃炎幽门螺杆菌300以上是阳性怎么办?", + "output": "有幽门螺杆菌感染救治当抗幽门螺杆菌建议三联疗法火四联疗法:两种抗生素奥美拉唑或两种抗生素奥美拉唑胃粘膜守护药除了及时救治胃炎外,患者朋友理应始终保持愉快的心态去直面疾病,只有这样才能降低患者的免疫力以及抗衡疾病的信念,同时要多看重自身饮食护理,多观注自身的症状变动,认为这样一定能将胃炎撵走。" + }, + { + "instruction": "你是一个心血管科医生,请根据患者的问题给出建议:外公一直在用降压药,不知晓可不可以吃蜂王浆,蜂王浆高血压能吃吗?", + "output": "您好蜂王浆这种保健品对于高血压患者是可以吃的,它对血压没下降作用,但是同时也不减低血压。老年人喝蜂王浆可以消化体内需要有的营养物质,对人体是有一定的好处的,对高血压患者需要有长期注意低盐低脂饮食,把烟酒癖好都要戒掉,要有规律的活动。以上可以做为参考。" + }, + { + "instruction": "你是一个肝病科医生,请根据患者的问题给出建议:我和男朋友相识4年,很恩爱,但是他有乙肝,和他做爱我会被感染吗", + "output": "如果有口腔溃疡就有可能感染的你可以打乙肝疫苗的,只要打了乙肝疫苗产生了抗体,就用不着忧心被感染了针对患者来说,肝病病情严重就要及时对症救治,恰当用药,否则会致使肝病再次病发,除此之外,患者还需要有恰当饮食,始终保持心情愉快,期望上述的问题答案可以协助到大家,谢谢。" + }, + { + "instruction": "你是一个神经科医生,请根据患者的问题给出建议:马上就要工作了但是还是自己感觉不信心虽然已经治疗过了又难过病发,癫痫能全愈吗", + "output": "癫痫病神经系统疾病,大多数的癫痫病需要有长期口服抗癫痫药物来控制。建议采用中西结合进行治疗,治疗癫痫病主要是预防出现意外伤害的再次发生,最好平时有家人陪伴。可以互相配合中医来辨证治疗的,平时应当饮食清淡不要吃辛辣油腻刺激性食物,不要有吸烟喝啤酒的不良习惯。" + }, + { + "instruction": "你是一个心血管科医生,请根据患者的问题给出建议:我有些高血压,学校近期要准备体测了,您好医生,高血压可以免体测吗?", + "output": "这个年龄再次出现高血压的情况,要戒备是否有肾性高血压的情况,注意仔细检查尿液和肾功能情况,测血压是可以半价的,到医院或学校医务室去检验血压,但是仔细检查血压前应休息半个小时左右,最好是早上监测数据比较准确,有必要做进一步仔细检查推测,注意情绪和饮食的调理。" + }, + { + "instruction": "你是一个心血管科医生,请根据患者的问题给出建议:我老公患高血压,这两天一直头痛,休息的时候也很难睡过去,医生,高血压会不会引来头痛?", + "output": "高血压是可以引来头痛的,一般高血压的主要表现出就是头痛头晕胸闷。有的也有可能会再次出现心慌。高血压引来头痛,主要是引来大脑内血压下降而引来颅内压下降致使的。治疗这种情况的话,主要是使用降压药物以及减低颅内压的药物实施治疗,最好是在医师的指导下使用。" + }, + { + "instruction": "你是一个呼吸科医生,请根据患者的问题给出建议:风寒感冒咳白痰怎么治疗?", + "output": "风寒感冒,咳有白痰的患者,主要症咳嗽痰多色白量多,兼具胸脘作闷,鼻塞,流清涕,全身酸痛,易疲劳等症状。临床诊断上常用杏苏散和二陈丸实施治疗。在用药物治疗的同时,应忌食辛辣刺激性的食物和寒凉性食物,多吃易消化营养丰富的食物,宜清淡饮食,留意歇息。" + }, + { + "instruction": "你是一个肝病科医生,请根据患者的问题给出建议:健康检查时医生说呈阳性,可能会是曱肝,您好您好曱肝与乙肝有区别吗?这个病严重吗?", + "output": "甲肝是由甲型肝炎病毒引来的,以肝脏炎症炎症为基础的传染病,主要通过粪-口途径散播,临床诊断上以疲乏,食欲减退,肝肿大,肝功能异常为主要表现出,部分病例再次出现黄疸,主要表现出为急性肝炎,无症状感染者常见。任何年龄均可患本病,但主要为儿童和青少年" + }, + { + "instruction": "你是一个心血管科医生,请根据患者的问题给出建议:我的老哥吧,他高血压拆分溃疡啊,我想问一下高血压拆分溃疡是怎么回事?", + "output": "高血压这种疾病再次出现问题的时候,一般对身体影响是比较大的,影响面也是比较广的,所以说有可能会引来一些变动的情况,而这种情况来说引来溃疡的几率非常低,不过如果一旦引来溃疡的问题,还是应当及时的减低血压,然后再治疗溃疡的问题。调节身心健康,保持自身卫生.避免因抵抗力下降而导致细菌入侵。" + }, + { + "instruction": "你是一个神经科医生,请根据患者的问题给出建议:其实是比较难受的,请问得癫痫病的人有有多少?", + "output": "癫痫的病发病因目前尚不完全明确。癫痫分成原发性癫痫和继发性癫痫。原发性癫痫可能会是由于隔代遗传因素,疾病,中毒,脑部发育不全等。不同的年龄段病发原因也各不相同。但是得了癫痫病不要太过紧张,目前为止癫痫病绝大部分都可以获得很好的控制。有的口服抗癫痫药物后甚至可以获得全愈。" + }, + { + "instruction": "你是一个神经科医生,请根据患者的问题给出建议:我感觉我家孩子的症状有点像癫痫,您好癫痫的自愈价格是多少?贵不贵?", + "output": "癫痫这种疾病由于是一种脑部神经网络生长发育异常的疾病,对于这种疾病现在还没有一次性完全治疗痊愈的药物,所以现在还不好确认治疗癫痫全愈的价格,这种疾病是需要有长期规律口服药物,所以长期接下来治疗的话是需要有一定的费用的。如果患癫痫这种疾病,一定要尽早去医院实施控制治疗。" + }, + { + "instruction": "你是一个消化科医生,请根据患者的问题给出建议:胃胀有食道裂孔疝十二指肠溃疡近期可能是焦虑症老胃胀", + "output": "你好!你的情况建议你用传统中药黑矾,黑枣,核桃仁,栀子,茯苓,砂仁,厚朴,三棱,穿山甲,寸曲,麦芽,上甲,下甲,红花,沉,铁胆粉,蜂胶,蜂蜜,蜂蜡救治,可以快速自愈;期望你正确的救治,早日康复。胃胀患者的日常救治主要是服食药物,患者可以用口服药物,互相配合维生素,而且留意规律作息,禁酒酒" + }, + { + "instruction": "你是一个神经科医生,请根据患者的问题给出建议:腹部癫痫是什么?我的小腹总是胀痛,经常都这样,也没吃什么不对的东西。", + "output": "腹部癫痫是一种以腹部症状为临床表现的癫痫。主要表现出为腹痛为突然复发或者是突然停止下来。常伴发恶心干呕,恶心呕吐等症状,复发时无意识失去。在治疗上主要是给患者口服抗癫痫的药物以及缓解并发症的症状,具体药物的用法用量,要在临床诊断医生的指导下运用。" + }, + { + "instruction": "你是一个肝病科医生,请根据患者的问题给出建议:如果我要检查艾滋、乙肝、丙肝?", + "output": "艾滋、乙肝、丙肝检验,抽一次静脉血,检查个传染系列即可。艾滋、乙肝、丙肝检验,抽一次静脉血,检查个传染系列即可。抽血所用器具都是一人个、单人单用,是绝不会感染的。传染系列的价格在300约莫。其实得了丙肝并不可怕,患者要认为自己是可以战胜病魔的,只要及时发现症状,并且对症救治,压制病情及康复的几率都非常大。" + }, + { + "instruction": "你是一个消化科医生,请根据患者的问题给出建议:干呕涨肚咳迷糊晕症状是不是胃病着凉因数啊?请指教谢谢", + "output": "考量胃炎及胃肠动力较差所致,可有腹痛腹痛,恶心干呕等一连串症状。建议饮食清淡,多饮水,留意歇息,可以服食抑酸药和胃动力药物纾解,积极救治感冒、。胃病患者在对症救治之外,患者在生活中还需要有留意始终保持恰当饮食的好习惯,消化身体营养,期望上述的答案可以协助到你,谢谢!" + }, + { + "instruction": "你是一个肝病科医生,请根据患者的问题给出建议:最近公司健康检查,查出来乙肝表面抗体是阳性是正常的吗?会不会是没抗体?", + "output": "您好朋友根据您的叙述症状,乙肝抗体阳性证明身体具备抵抗力。您好朋友根据您的叙述症状,证明您的身体具备乙肝抗体没问题,拥有健康的身体。乙肝患者的日常救治主要是对症救治,患者可以用许多口服药物,互相配合许多维生素,而且留意规律作息,禁烟酒,期望乙肝患者可以尽快康复!谢谢!" + }, + { + "instruction": "你是一个心血管科医生,请根据患者的问题给出建议:100-160高血压吃什么药?父亲以前没发现自己得了高血压,现在应当吃什么药呢?", + "output": "根据您目前叙述的情况,目前主要是发觉血压下降,您目前叙述的血压下降数值还是比较明显的达到高血压二级,再次出现这种情况容易致使心脑血管风险,要增强,建议可以在医生指导下口服钙离子拮抗剂来减低血压,平时建议注意休息,祝您健康。以上可以做为参考。" + }, + { + "instruction": "你是一个肝病科医生,请根据患者的问题给出建议:乙肝五项结果245呈阳性打过五次乙肝疫苗,这样的结果是传染乙肝了吗", + "output": "你好!根据你的报告单来看结果是你曾经有传染过乙肝病毒!现在的结果是有了抗体!而不是传染乙肝病毒。所以用不着忧心。期望我的解惑对你有所协助乙肝的治疗方法有许多,但是由于患者病情不同所以采用的诊病方法也就不一样,因此患者需要及时检查诊断,方才能对症救治。。" + }, + { + "instruction": "你是一个肝病科医生,请根据患者的问题给出建议:乙肝;肝硬化;肝腹水;脂肪肝", + "output": "乙肝的散播途径主要有三条,性传播、血液散播、母婴横向散播,如果再次出现肝硬化、肝腹水,考量有乙肝病毒拷贝,这时通过性交是可以传染给对方的。除了救治乙肝外,建议患者在日常生活中要留意饮食,并且要始终保持舒畅的心情,以此来增强体质放慢康复速度。" + }, + { + "instruction": "你是一个心血管科医生,请根据患者的问题给出建议:我有个姐姐她这么年轻就有高血压,而且已经血管破裂,高血压引来血管破裂怎么办?", + "output": "高血压引来血管破裂多是致使脑出血,脑出血的原因大多数是老龄、高血压和动脉硬化所引发的脑出血。年轻人脑出血,若无基础疾病及外伤,一般多考虑脑血管疾病,如动脉瘤,动静脉畸形等,除了血液性疾病,如白血病引发的出血性脑卒中。新生儿脑出血主要是由于孕期的维生素K缺乏和产伤引发的脑出血。" + }, + { + "instruction": "你是一个神经科医生,请根据患者的问题给出建议:我有个朋友,他得了癫痫症,最近他还复发了,您好癫痫类型有哪些?", + "output": "癫痫类型有全部性的,的局部性的,有完全性的,一般患了癫痫病主要要作好防御措施才行的,因为这种病复发下来哪里倒地,会伤到自已的身体,所以家人一定要注意了,防复发时没人在身边引发不急时治疗引发生命危险的,所以癫痫病有很多种不同,也不知晓什么时候复发的。" + }, + { + "instruction": "你是一个消化科医生,请根据患者的问题给出建议:可以治好我的胃胀胃炎吗", + "output": "你的情况考虑是慢性胃炎或者消化性溃疡引来的,需要有积极胃镜仔细检查看一看。可以服食药物看看。平时留意规律饮食,禁酒酒,禁极冷酸辣食物,不要暴饮暴食胃胀的治疗方法有,但是由于患者病情不同所以采用的诊病方法也就不一样,因此需要有患者尽快诊断,方才能对症下药。。" + }, + { + "instruction": "你是一个呼吸科医生,请根据患者的问题给出建议:感冒咳胸部麻木15天是怎么回事?", + "output": "感冒咳再次出现胸部麻木的症状,一般都是由于遭到了一定的影响才会引来的,这时候有可能是长时间的异常受力引来了局部神经损伤,或者是说因为长期的咳所引来的,所以说这种情况只有咳停止下来之后,症状才会慢慢彻底恢复,一般可以使用甘草片或者是清肺糖浆来治疗。" + }, + { + "instruction": "你是一个心血管科医生,请根据患者的问题给出建议:我最近身体不舒服,看见电视上有护理高血压的情景剧,所以我想您好护理高血压情景剧是真的吗?", + "output": "您好,高血压情景剧毕竟只是情景剧,而不是科教片,所以,里面所说的造成护理高血压的方法,不能够完全误信,还是要遵从专业医生的建议。建议高血压患者平时饮食宜清淡,不要过咸及油腻食物,适当多吃蔬菜水果,以防情绪震荡,不要过度操劳,适当运动,控制体重。" + }, + { + "instruction": "你是一个心血管科医生,请根据患者的问题给出建议:我平时很喜欢吃甜的食物,最近被查出来患高血压,您好医生高血压可以吃甜的吗?", + "output": "高血压可以吃甜食的,但是经常吃甜食有利于身体健康,有可能会引来糖尿病的,所以在平时需要有低糖饮食,低盐饮食,预防工作高血压的缓解,对于高血压,相信与隔代遗传,身体肥胖也是有关系,所以需要有尽快积极的治疗,可以口服硝苯地平缓释片,对身体是有好处的。" + }, + { + "instruction": "你是一个肝病科医生,请根据患者的问题给出建议:小孩3岁了,如何以防肝炎感染?因为我们都有肝炎,要常给孩子做健康检查吗?", + "output": "肝炎的类型比较多,您好您具体是哪种类型的肝炎?甲肝,乙肝,还是丙肝?肝炎患者的日常救治主要是服食药物,患者可以用许多口服药物,互相配合许多维生素,而且留意自身护理,恰当饮食,防止寒冷食物,期望肝炎患者可以尽快康复!建议你带孩子去正规专业的医院进行救治。" + }, + { + "instruction": "你是一个神经科医生,请根据患者的问题给出建议:我想理解一下治疗癫痫病的有几种?可有可以一次性就全愈的治疗方法?", + "output": "癫痫在全国范围之内都是个非常棘手的疾病,因为他没很好的方法能治疗痊愈,只好通过药物控制来缓解癫痫的复发,如果患者一年复发低于两次的话,就建议服药来控制,因为癫痫的复发实际上症状比较可怕,会给患者引发极大的心理干扰。目前并没有个药物能完全一次性治愈癫痫。" + }, + { + "instruction": "你是一个肝病科医生,请根据患者的问题给出建议:已经怀孕2个月了,在婚检的时候查的出的可能会患小三阳。", + "output": "只要经过救治,肝功正常,病毒量阴性就是可以要孩子的。针对患者来说,肝病病情严重就要及时对症救治,恰当用药,否则会致使肝病再次病发,除此之外,患者还需要有看重自身的饮食以及心理护理,建议时刻保持良好的心态,期望上述的问题答案可以协助到大家,谢谢。" + }, + { + "instruction": "你是一个肝病科医生,请根据患者的问题给出建议:小三阳患者谷丙转氨酶75dna大于10的5次方怎么办", + "output": "治乙肝西医目前没特效药,中医中药长期临床实践累积了许多独特的奇方秘方,建议你用传统中药!对于肝病严重患者来说,建议马上就诊,根据医生的意见来马上救治,不要盲目误信广告药物来救治,以免令得病情严重,以上意见仅供参考。望上述的答案可以协助到您,谢谢。" + }, + { + "instruction": "你是一个肝病科医生,请根据患者的问题给出建议:最近肚脐右边随呼息有一点痛.而且右边稍微大于左边.严重吗?", + "output": "找为好中医,看一看病根在哪,从根本上救治,坚持下来有自愈的可能性除了及时救治肝病外,患者朋友理应始终保持积极的心态去直面疾病,只有这样才能令得患者及时对症救治,同时要多看重自身饮食护理,观注营养均衡,及时消化身体营养,认为这样一定能将肝病撵走。" + }, + { + "instruction": "你是一个神经科医生,请根据患者的问题给出建议:总是不舒服,难受,睡不着呢。抽动症和癫痫有什么区别呢?", + "output": "您好,很高兴为您解惑。抽动症症状更轻许多,一般不会引来神智发生改变,不会突然倒地及尿便失禁,不会有意识失去。而癫痫大发作多会再次出现短暂的意识丧失,牙关紧闭,口吐白沫,尿便失禁,痛觉彻底恢复后无法正确叙述。脑电图检查可以甄别,抽动症孩子绝大部分脑电图正常,而癫痫儿童则会再次出现脑电图异常。" + }, + { + "instruction": "你是一个消化科医生,请根据患者的问题给出建议:慢性胃炎可以通过活动稳定吗?因为我经常暧气,喝药效果不大", + "output": "留意口腔卫生,口服消炎药,局部留意清洁建议及时实施对症救治,多喝水,留意防寒,清淡容易吸收的食物其实得了胃炎并不可怕,患者要认为自己是可以战胜病魔的,只要及时发现症状,并且对症救治,纾解病情及康复的几率都非常大。用些消炎牙膏刷牙,早晚个刷一次牙,保持口腔卫生。" + }, + { + "instruction": "你是一个心血管科医生,请根据患者的问题给出建议:我妈妈血压一直有点高,可以吃三七粉的吗?高血压吃三七粉可以吗?", + "output": "高血压可以吃三七粉的,适当的吃一些是没关系的,用不着太担心,但是三七粉对于高血压患者来说并不能起些治疗的作用,建议高血压患者平时还是要注意监控器血压,不定期的测定血压,在口服药物的同时在饮食方面尽量要办到低脂低盐的饮食方式,多吃一些新鲜的蔬菜和容易吸收的食物" + }, + { + "instruction": "你是一个神经科医生,请根据患者的问题给出建议:各位医生好,这几天腿一直抽动,晚上特别严重,想问问最近腿老是抽动,抽动是癫痫吗?", + "output": "单纯的腿抽筋、抽动,不像是癫痫致使的,而像是缺钙引来的或者受寒引来的,平时需要有留意防寒,多歇息才可以的。建议,这种情况可以在防寒的基础上口服钙剂或者含钙的口服液,忌服辛辣、受寒的食物,多吃香蕉,多吃粗粮。严重的抽动则需要有去医院检验、诊病。" + }, + { + "instruction": "你是一个消化科医生,请根据患者的问题给出建议:C14幽门螺杆菌9.70是拥有正常?还是严重还是算不上很高?", + "output": "碳14呼气试验的参考值大概是在40,有的参考值在100。如果低于上述的数值,都是阴性的。所以你的9.70是阴性的。证明没幽门螺杆菌的传染。所以是正常的。幽门螺杆菌病情很严重,建议患者马上实行救治,期望患者可以根据医生的意见对症救治。同时看重饮食问题,防止辛辣打击食物。" + }, + { + "instruction": "你是一个肝病科医生,请根据患者的问题给出建议:我上次健康检查了,乙肝表面抗体是阳性,结果说:乙肝病毒已产生一定免疫力是什么意思", + "output": "两对半第二项阳性对此你体内有保护性的抗体,麻醉乙肝疫苗就是使这项转成阳性,是正常的。肝病患者在对症救治之外,患者在生活中还需要有留意要保持良好的心情,好的心情对疾病的彻底恢复很有协助,建议去正规专业的医院进行救治,期望上述的答案可以协助到你" + }, + { + "instruction": "你是一个心血管科医生,请根据患者的问题给出建议:我高血压两年了,一直忍耐喝药,感觉有点虚,想吃点参补一补,讨教下高血压可以吃丹参粉吗?", + "output": "高血压是可以喝丹参粉的,但是一定要不要过量。一天极少量喝是可以的,它是活血化瘀的药物,能稳定机体内血流情况减低血粘度,减低外周血管阻力,从而使血压下降有辅助降压的效用,但是一定要适量不要过量。平时低盐低脂饮食,少吃油腻油炸食物,饮食以清淡为基础。" + }, + { + "instruction": "你是一个心血管科医生,请根据患者的问题给出建议:老公最近因为高血压头晕的厉害还伴发头连续个礼拜了还没有转想问问高血压引来的头晕该怎么办?", + "output": "高血压是指动脉内血液压力过高。本病是临床诊断常见的全身血管性疾病。如果头晕是高血压引来那首先需要有控制血始终保持血压平稳。血压控制住头晕慢慢的也就可以缓解。建议遵遵医嘱长期服食降压药不随意加减药物剂留意药物的不良反再次出现不适症状及时复诊。" + }, + { + "instruction": "你是一个心血管科医生,请根据患者的问题给出建议:这两年年纪大了,血压也已经开始下跌,据说叶酸可以降血压,您好高血压吃叶酸有效吗?", + "output": "如果血压下降的话建议你先通过仔细检查明确是原发性高血压还是继发性高血压的,其中高血压类型有一种是拥有叶酸缺乏性的高血压叫作h型高血压,通过仔细检查同型半胱氨酸可以明确的,如果仔细检查明确后,就要通过消化叶酸治疗了,同时也要口服降压药物治疗的,平时要监测数据血压水平。" + }, + { + "instruction": "你是一个心血管科医生,请根据患者的问题给出建议:我患高血压五六年啦,天天喝药吃烦啦,哪种东西能根治高血压,高血压克星是什么?", + "output": "高血压的患者可以吃许多新鲜的水果蔬菜或者是芹菜山药之类的食物,可以起些降血压的作用,另外高血压的患者平时也应当注意低盐,低脂,低胆固醇饮食,适当的实施体育运动和锻练高血压的患者还应当在医生的指导下口服降血压的药物,断然不可擅自停药,防止对血压引发影响。" + } +] From af857a7ddf682e22b6326998104c6b3b8063c69c Mon Sep 17 00:00:00 2001 From: LiangQuan Date: Mon, 1 Jul 2024 14:24:36 +0800 Subject: [PATCH 33/59] feat: add `url_suffix` parameter to `predictor.openai` (#30) --- pai/predictor.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/pai/predictor.py b/pai/predictor.py index 7138996..f5d1a51 100644 --- a/pai/predictor.py +++ b/pai/predictor.py @@ -731,13 +731,16 @@ def raw_predict( ) return resp - def openai(self, **kwargs) -> "OpenAI": + def openai(self, url_suffix: str = "v1", **kwargs) -> "OpenAI": """Initialize an OpenAI client from the predictor. Only used for OpenAI API compatible services, such as Large Language Model service from PAI QuickStart. Args: + url_suffix (str, optional): URL suffix that will be appended to the + EAS service endpoint to form the base URL for the OpenAI client. + (Default "v1"). **kwargs: Additional keyword arguments for the OpenAI client. Returns: @@ -749,7 +752,11 @@ def openai(self, **kwargs) -> "OpenAI": "openai package is not installed, install it with `pip install openai`." ) - base_url = kwargs.pop("base_url", posixpath.join(self.endpoint + "v1/")) + if url_suffix.startswith("/"): + default_base_url = posixpath.join(self.endpoint, url_suffix[1:]) + else: + default_base_url = posixpath.join(self.endpoint, url_suffix) + base_url = kwargs.pop("base_url", default_base_url) api_key = kwargs.pop("api_key", self.access_token) return OpenAI(base_url=base_url, api_key=api_key, **kwargs) From be7962b56dfb8a4dcd80396abf35c07673991225 Mon Sep 17 00:00:00 2001 From: LiangQuan Date: Mon, 1 Jul 2024 14:32:19 +0800 Subject: [PATCH 34/59] release: 0.4.8 (#31) --- pai/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pai/version.py b/pai/version.py index 7b413b3..32f748d 100644 --- a/pai/version.py +++ b/pai/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -VERSION = "0.4.8.dev0" +VERSION = "0.4.8" From 1dd66c0342e5aead03cf44cd7b2980ea14be6ea8 Mon Sep 17 00:00:00 2001 From: LiangQuan Date: Thu, 11 Jul 2024 09:41:03 +0800 Subject: [PATCH 35/59] feat: spot instance/job settings supports. (#33) * bump to develop version: 0.4.9.dev0 * chore: upgrade trainingservice pop sdk * feat: trainingjob supports use spot instance * feat: model recipe support use_spot_instance * feat: support advance settings for jobs * hack: add `resource_type` field for CreateTrainingJobRequest * fix type annotation for `resource_type` * fix: model recipe missing input/output channels --- pai/api/training_job.py | 18 + pai/estimator.py | 16 + pai/job/__init__.py | 6 + pai/job/_training_job.py | 56 +- .../alibabacloud_paistudio20220112/client.py | 4710 ++++- .../alibabacloud_paistudio20220112/models.py | 14807 ++++++++-------- pai/model/_model_recipe.py | 72 +- pai/processor.py | 18 +- pai/version.py | 2 +- tests/integration/test_estimator.py | 21 +- .../test_model/test_model_recipe.py | 26 +- tests/integration/utils.py | 4 + 12 files changed, 10952 insertions(+), 8804 deletions(-) diff --git a/pai/api/training_job.py b/pai/api/training_job.py index f8648fa..25697c8 100644 --- a/pai/api/training_job.py +++ b/pai/api/training_job.py @@ -20,12 +20,14 @@ CreateTrainingJobRequest, CreateTrainingJobRequestComputeResource, CreateTrainingJobRequestComputeResourceInstanceSpec, + CreateTrainingJobRequestComputeResourceSpotSpec, CreateTrainingJobRequestExperimentConfig, CreateTrainingJobRequestHyperParameters, CreateTrainingJobRequestInputChannels, CreateTrainingJobRequestLabels, CreateTrainingJobRequestOutputChannels, CreateTrainingJobRequestScheduler, + CreateTrainingJobRequestSettings, CreateTrainingJobRequestUserVpc, CreateTrainingJobResponseBody, GetTrainingJobRequest, @@ -86,8 +88,10 @@ def create( instance_type, instance_count, job_name, + spot_spec: Optional[Dict[str, Any]] = None, instance_spec: Optional[Dict[str, str]] = None, resource_id: Optional[str] = None, + resource_type: Optional[str] = None, hyperparameters: Optional[Dict[str, Any]] = None, input_channels: Optional[List[Dict[str, Any]]] = None, output_channels: Optional[List[Dict[str, Any]]] = None, @@ -102,6 +106,7 @@ def create( algorithm_spec: Optional[Dict[str, Any]] = None, user_vpc_config: Optional[Dict[str, Any]] = None, experiment_config: Optional[Dict[str, Any]] = None, + settings: Optional[Dict[str, Any]] = None, ) -> str: """Create a TrainingJob.""" if algorithm_spec and ( @@ -126,9 +131,16 @@ def create( for ch in output_channels ] if instance_type: + spot_spec = ( + CreateTrainingJobRequestComputeResourceSpotSpec().from_map(spot_spec) + if spot_spec + else None + ) compute_resource = CreateTrainingJobRequestComputeResource( ecs_count=instance_count, ecs_spec=instance_type, + use_spot_instance=bool(spot_spec), + spot_spec=spot_spec, ) elif instance_spec: compute_resource = CreateTrainingJobRequestComputeResource( @@ -169,6 +181,7 @@ def create( compute_resource=compute_resource, hyper_parameters=hyper_parameters, input_channels=input_channels, + resource_type=resource_type, environments=environments, python_requirements=requirements, labels=labels, @@ -181,6 +194,11 @@ def create( experiment_config=CreateTrainingJobRequestExperimentConfig().from_map( experiment_config ), + settings=( + CreateTrainingJobRequestSettings().from_map(settings) + if settings + else None + ), ) resp: CreateTrainingJobResponseBody = self._do_request( diff --git a/pai/estimator.py b/pai/estimator.py index a3ee149..58a539d 100644 --- a/pai/estimator.py +++ b/pai/estimator.py @@ -35,6 +35,8 @@ DEFAULT_OUTPUT_MODEL_CHANNEL_NAME, DEFAULT_TENSORBOARD_CHANNEL_NAME, ExperimentConfig, + ResourceType, + SpotSpec, UserVpcConfig, ) from .model import InferenceSpec, Model, ResourceConfig @@ -187,11 +189,14 @@ def __init__( environments: Optional[Dict[str, str]] = None, requirements: Optional[List[str]] = None, instance_type: Optional[str] = None, + spot_spec: Optional[SpotSpec] = None, instance_spec: Optional[Dict] = None, resource_id: Optional[Dict] = None, + resource_type: Optional[Union[str, ResourceType]] = None, instance_count: Optional[int] = None, user_vpc_config: Optional[UserVpcConfig] = None, experiment_config: Optional[ExperimentConfig] = None, + settings: Optional[Dict[str, Any]] = None, labels: Optional[Dict[str, str]] = None, session: Optional[Session] = None, ): @@ -252,12 +257,18 @@ def __init__( 'package' or 'package==version'. This is similar to the contents of a requirements.txt file used in Python projects. If requirements.txt is provided in user code directory, requirements will override the conflict dependencies directly. + resource_type (str, optional): The resource type used to run the training job. + By default, general computing resource is used. If the resource_type is + 'Lingjun', Lingjun computing resource is used. instance_type (str, optional): The machine instance type used to run the training job. To view the supported machine instance types, please refer to the document: https://help.aliyun.com/document_detail/171758.htm#section-55y-4tq-84y. If the instance_type is "local", the training job is executed locally using docker. + spot_spec (:class:`pai.job.SpotSpec`, optional): The specification of the spot + instance used to run the training job. If provided, the training job will + use the spot instance to run the training job. instance_count (int): The number of machines used to run the training job. user_vpc_config (:class:`pai.estimator.UserVpcConfig`, optional): The VPC configuration used to enable the training job instance to connect to the @@ -270,6 +281,8 @@ def __init__( training job and the experiment. If provided, the training job will belong to the specified experiment, in which case the training job will use artifact_uri of experiment as default output path. Default to None. + settings (dict, optional): A dictionary that represents the additional settings + for job, such as AIMaster configurations. labels (Dict[str, str], optional): A dictionary that maps label names to their values. This optional field allows you to provide a set of labels that will be applied to the training job. @@ -287,11 +300,14 @@ def __init__( instance_type=instance_type, instance_count=instance_count, resource_id=resource_id, + resource_type=resource_type, + spot_spec=spot_spec, instance_spec=instance_spec, user_vpc_config=user_vpc_config, max_run_time=max_run_time, environments=environments, requirements=requirements, + settings=settings, labels=labels, ) diff --git a/pai/job/__init__.py b/pai/job/__init__.py index 6d2769c..239b3ff 100644 --- a/pai/job/__init__.py +++ b/pai/job/__init__.py @@ -22,6 +22,9 @@ InstanceSpec, ModelRecipeSpec, OssLocation, + ResourceType, + SpotSpec, + SpotStrategy, TrainingJob, TrainingJobStatus, UriInput, @@ -45,4 +48,7 @@ "ExperimentConfig", "InstanceSpec", "UriInput", + "SpotSpec", + "ResourceType", + "SpotStrategy", ] diff --git a/pai/job/_training_job.py b/pai/job/_training_job.py index 8cfc57a..6af5e60 100644 --- a/pai/job/_training_job.py +++ b/pai/job/_training_job.py @@ -17,6 +17,7 @@ import time import typing from concurrent.futures import ThreadPoolExecutor +from enum import Enum from typing import Any, Dict, List, Optional, Union from pydantic import BaseModel, ConfigDict, Field @@ -55,6 +56,19 @@ def as_oss_dir_uri(uri: str): DEFAULT_TENSORBOARD_CHANNEL_NAME = "tensorboard" +class SpotStrategy(str, Enum): + SpotWithPriceLimit = "SpotWithPriceLimit" + SpotAsPriceGo = "SpotAsPriceGo" + + def __repr__(self): + return self.value + + +class ResourceType(str, Enum): + Lingjun = "Lingjun" + General = "General" + + class BaseAPIModel(BaseModel): model_config = ConfigDict( @@ -275,11 +289,14 @@ class AlgorithmSpec(BaseAPIModel): ) hyperparameter_definitions: List[HyperParameterDefinition] = Field( default_factory=list, - alias="HyperParameter", + alias="HyperParameters", description="Hyperparameter definitions.", ) job_type: str = Field(default="PyTorchJob") code_dir: Optional[CodeDir] = Field(None, description="Source code location.") + customization: Optional[Dict[str, Any]] = Field( + None, description="Whether the algorithm supports customize code." + ) class ModelRecipeSpec(BaseAPIModel): @@ -300,6 +317,19 @@ class ModelRecipeSpec(BaseAPIModel): requirements: Optional[List[str]] = None +class SpotSpec(BaseAPIModel): + spot_strategy: SpotStrategy = Field( + ..., + description="Spot instance strategy, support 'SpotWithPriceLimit', 'SpotAsPriceGo'", + ) + spot_discount_limit: Optional[float] = Field( + None, + description="Spot instance discount limit, maximum 2 decimal places, " + "required when spot_strategy is 'SpotWithPriceLimit'." + "For example, 0.5 means 50% off the original price.", + ) + + class TrainingJob(BaseAPIModel): """TrainingJob represents a training job in the PAI service.""" @@ -542,23 +572,29 @@ def __init__( instance_spec: Optional[Dict] = None, instance_count: Optional[int] = None, resource_id: Optional[Dict] = None, + resource_type: Optional[Union[str, ResourceType]] = None, + spot_spec: Optional[SpotSpec] = None, environments: Optional[Dict] = None, requirements: Optional[List[str]] = None, labels: Optional[Dict[str, str]] = None, + settings: Optional[Dict[str, Any]] = None, ): self.session = get_default_session() self._training_jobs = [] self.base_job_name = base_job_name or type(self).__name__.lower() self.output_path = output_path self.user_vpc_config = user_vpc_config + self.spot_spec = spot_spec self.experiment_config = experiment_config self.max_run_time = max_run_time self.instance_type = instance_type self.instance_spec = instance_spec self.instance_count = instance_count or 1 self.resource_id = resource_id + self.resource_type = ResourceType(resource_type) if resource_type else None self.environments = environments self.requirements = requirements + self.settings = settings self.labels = labels def wait(self, interval: int = 5, show_logs: bool = True, all_jobs: bool = False): @@ -704,6 +740,7 @@ def build_outputs( return [item.model_dump() for item in res] + # TODO: get arguments, such as VPCConfig, instance_type etc, from self instance. def _submit( self, job_name: str, @@ -728,6 +765,20 @@ def _submit( show_logs: bool = False, ): session = get_default_session() + + if not self.resource_type or self.resource_type == ResourceType.General: + resource_type = None + else: + resource_type = self.resource_type.value + + if self.spot_spec: + spot_spec = { + "SpotStrategy": self.spot_spec.spot_strategy.value, + } + if self.spot_spec.spot_discount_limit: + spot_spec["SpotDiscountLimit"] = self.spot_spec.spot_discount_limit + else: + spot_spec = None training_job_id = session.training_job_api.create( instance_count=instance_count, instance_spec=instance_spec.model_dump() if instance_spec else None, @@ -738,9 +789,11 @@ def _submit( if experiment_config and isinstance(experiment_config, ExperimentConfig) else experiment_config ), + spot_spec=spot_spec, algorithm_version=algorithm_version, instance_type=instance_type, resource_id=resource_id, + resource_type=resource_type, job_name=job_name, hyperparameters=hyperparameters, max_running_in_seconds=max_run_time, @@ -751,6 +804,7 @@ def _submit( user_vpc_config=user_vpc_config, labels=labels, environments=environments, + settings=self.settings, ) training_job = TrainingJob.get(training_job_id) self._training_jobs.append(training_job) diff --git a/pai/libs/alibabacloud_paistudio20220112/client.py b/pai/libs/alibabacloud_paistudio20220112/client.py index b89d438..63832fb 100644 --- a/pai/libs/alibabacloud_paistudio20220112/client.py +++ b/pai/libs/alibabacloud_paistudio20220112/client.py @@ -18,7 +18,7 @@ class Client(OpenApiClient): *\ """ def __init__( - self, + self, config: open_api_models.Config, ): super().__init__(config) @@ -60,102 +60,6 @@ def get_endpoint( return endpoint_map.get(region_id) return EndpointUtilClient.get_endpoint_rules(product_id, region_id, endpoint_rule, network, suffix) - def build_llmsnapshot_with_options( - self, - project_id: str, - snapshot_id: str, - request: pai_studio_20220112_models.BuildLLMSnapshotRequest, - headers: Dict[str, str], - runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.BuildLLMSnapshotResponse: - UtilClient.validate_model(request) - body = {} - if not UtilClient.is_unset(request.description): - body['Description'] = request.description - if not UtilClient.is_unset(request.display_name): - body['DisplayName'] = request.display_name - if not UtilClient.is_unset(request.labels): - body['Labels'] = request.labels - if not UtilClient.is_unset(request.workload): - body['Workload'] = request.workload - req = open_api_models.OpenApiRequest( - headers=headers, - body=OpenApiUtilClient.parse_to_map(body) - ) - params = open_api_models.Params( - action='BuildLLMSnapshot', - version='2022-01-12', - protocol='HTTPS', - pathname=f'/api/v1/langstudio/projects/{OpenApiUtilClient.get_encode_param(project_id)}/snapshots/{OpenApiUtilClient.get_encode_param(snapshot_id)}/build', - method='PUT', - auth_type='AK', - style='ROA', - req_body_type='json', - body_type='json' - ) - return TeaCore.from_map( - pai_studio_20220112_models.BuildLLMSnapshotResponse(), - self.call_api(params, req, runtime) - ) - - async def build_llmsnapshot_with_options_async( - self, - project_id: str, - snapshot_id: str, - request: pai_studio_20220112_models.BuildLLMSnapshotRequest, - headers: Dict[str, str], - runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.BuildLLMSnapshotResponse: - UtilClient.validate_model(request) - body = {} - if not UtilClient.is_unset(request.description): - body['Description'] = request.description - if not UtilClient.is_unset(request.display_name): - body['DisplayName'] = request.display_name - if not UtilClient.is_unset(request.labels): - body['Labels'] = request.labels - if not UtilClient.is_unset(request.workload): - body['Workload'] = request.workload - req = open_api_models.OpenApiRequest( - headers=headers, - body=OpenApiUtilClient.parse_to_map(body) - ) - params = open_api_models.Params( - action='BuildLLMSnapshot', - version='2022-01-12', - protocol='HTTPS', - pathname=f'/api/v1/langstudio/projects/{OpenApiUtilClient.get_encode_param(project_id)}/snapshots/{OpenApiUtilClient.get_encode_param(snapshot_id)}/build', - method='PUT', - auth_type='AK', - style='ROA', - req_body_type='json', - body_type='json' - ) - return TeaCore.from_map( - pai_studio_20220112_models.BuildLLMSnapshotResponse(), - await self.call_api_async(params, req, runtime) - ) - - def build_llmsnapshot( - self, - project_id: str, - snapshot_id: str, - request: pai_studio_20220112_models.BuildLLMSnapshotRequest, - ) -> pai_studio_20220112_models.BuildLLMSnapshotResponse: - runtime = util_models.RuntimeOptions() - headers = {} - return self.build_llmsnapshot_with_options(project_id, snapshot_id, request, headers, runtime) - - async def build_llmsnapshot_async( - self, - project_id: str, - snapshot_id: str, - request: pai_studio_20220112_models.BuildLLMSnapshotRequest, - ) -> pai_studio_20220112_models.BuildLLMSnapshotResponse: - runtime = util_models.RuntimeOptions() - headers = {} - return await self.build_llmsnapshot_with_options_async(project_id, snapshot_id, request, headers, runtime) - def check_instance_web_terminal_with_options( self, training_job_id: str, @@ -164,6 +68,14 @@ def check_instance_web_terminal_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.CheckInstanceWebTerminalResponse: + """ + @summary 检查WebTerminal + + @param request: CheckInstanceWebTerminalRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: CheckInstanceWebTerminalResponse + """ UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.check_info): @@ -196,6 +108,14 @@ async def check_instance_web_terminal_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.CheckInstanceWebTerminalResponse: + """ + @summary 检查WebTerminal + + @param request: CheckInstanceWebTerminalRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: CheckInstanceWebTerminalResponse + """ UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.check_info): @@ -226,6 +146,12 @@ def check_instance_web_terminal( instance_id: str, request: pai_studio_20220112_models.CheckInstanceWebTerminalRequest, ) -> pai_studio_20220112_models.CheckInstanceWebTerminalResponse: + """ + @summary 检查WebTerminal + + @param request: CheckInstanceWebTerminalRequest + @return: CheckInstanceWebTerminalResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.check_instance_web_terminal_with_options(training_job_id, instance_id, request, headers, runtime) @@ -236,6 +162,12 @@ async def check_instance_web_terminal_async( instance_id: str, request: pai_studio_20220112_models.CheckInstanceWebTerminalRequest, ) -> pai_studio_20220112_models.CheckInstanceWebTerminalResponse: + """ + @summary 检查WebTerminal + + @param request: CheckInstanceWebTerminalRequest + @return: CheckInstanceWebTerminalResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.check_instance_web_terminal_with_options_async(training_job_id, instance_id, request, headers, runtime) @@ -245,6 +177,13 @@ def create_ai4ddefault_bucket_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.CreateAI4DDefaultBucketResponse: + """ + @summary 创建AI4D模型桶 + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: CreateAI4DDefaultBucketResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -269,6 +208,13 @@ async def create_ai4ddefault_bucket_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.CreateAI4DDefaultBucketResponse: + """ + @summary 创建AI4D模型桶 + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: CreateAI4DDefaultBucketResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -289,11 +235,21 @@ async def create_ai4ddefault_bucket_with_options_async( ) def create_ai4ddefault_bucket(self) -> pai_studio_20220112_models.CreateAI4DDefaultBucketResponse: + """ + @summary 创建AI4D模型桶 + + @return: CreateAI4DDefaultBucketResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.create_ai4ddefault_bucket_with_options(headers, runtime) async def create_ai4ddefault_bucket_async(self) -> pai_studio_20220112_models.CreateAI4DDefaultBucketResponse: + """ + @summary 创建AI4D模型桶 + + @return: CreateAI4DDefaultBucketResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.create_ai4ddefault_bucket_with_options_async(headers, runtime) @@ -304,6 +260,14 @@ def create_ai4dserivce_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.CreateAI4DSerivceResponse: + """ + @summary 创建AI4D服务 + + @param request: CreateAI4DSerivceRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: CreateAI4DSerivceResponse + """ UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.inference_spec): @@ -338,6 +302,14 @@ async def create_ai4dserivce_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.CreateAI4DSerivceResponse: + """ + @summary 创建AI4D服务 + + @param request: CreateAI4DSerivceRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: CreateAI4DSerivceResponse + """ UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.inference_spec): @@ -370,6 +342,12 @@ def create_ai4dserivce( self, request: pai_studio_20220112_models.CreateAI4DSerivceRequest, ) -> pai_studio_20220112_models.CreateAI4DSerivceResponse: + """ + @summary 创建AI4D服务 + + @param request: CreateAI4DSerivceRequest + @return: CreateAI4DSerivceResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.create_ai4dserivce_with_options(request, headers, runtime) @@ -378,6 +356,12 @@ async def create_ai4dserivce_async( self, request: pai_studio_20220112_models.CreateAI4DSerivceRequest, ) -> pai_studio_20220112_models.CreateAI4DSerivceResponse: + """ + @summary 创建AI4D服务 + + @param request: CreateAI4DSerivceRequest + @return: CreateAI4DSerivceResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.create_ai4dserivce_with_options_async(request, headers, runtime) @@ -388,6 +372,14 @@ def create_algorithm_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.CreateAlgorithmResponse: + """ + @summary 创建新的算法 + + @param request: CreateAlgorithmRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: CreateAlgorithmResponse + """ UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.algorithm_description): @@ -424,6 +416,14 @@ async def create_algorithm_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.CreateAlgorithmResponse: + """ + @summary 创建新的算法 + + @param request: CreateAlgorithmRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: CreateAlgorithmResponse + """ UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.algorithm_description): @@ -458,6 +458,12 @@ def create_algorithm( self, request: pai_studio_20220112_models.CreateAlgorithmRequest, ) -> pai_studio_20220112_models.CreateAlgorithmResponse: + """ + @summary 创建新的算法 + + @param request: CreateAlgorithmRequest + @return: CreateAlgorithmResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.create_algorithm_with_options(request, headers, runtime) @@ -466,6 +472,12 @@ async def create_algorithm_async( self, request: pai_studio_20220112_models.CreateAlgorithmRequest, ) -> pai_studio_20220112_models.CreateAlgorithmResponse: + """ + @summary 创建新的算法 + + @param request: CreateAlgorithmRequest + @return: CreateAlgorithmResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.create_algorithm_with_options_async(request, headers, runtime) @@ -478,6 +490,14 @@ def create_algorithm_version_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.CreateAlgorithmVersionResponse: + """ + @summary 创建一个新的算法版本 + + @param tmp_req: CreateAlgorithmVersionRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: CreateAlgorithmVersionResponse + """ UtilClient.validate_model(tmp_req) request = pai_studio_20220112_models.CreateAlgorithmVersionShrinkRequest() OpenApiUtilClient.convert(tmp_req, request) @@ -514,6 +534,14 @@ async def create_algorithm_version_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.CreateAlgorithmVersionResponse: + """ + @summary 创建一个新的算法版本 + + @param tmp_req: CreateAlgorithmVersionRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: CreateAlgorithmVersionResponse + """ UtilClient.validate_model(tmp_req) request = pai_studio_20220112_models.CreateAlgorithmVersionShrinkRequest() OpenApiUtilClient.convert(tmp_req, request) @@ -548,6 +576,12 @@ def create_algorithm_version( algorithm_version: str, request: pai_studio_20220112_models.CreateAlgorithmVersionRequest, ) -> pai_studio_20220112_models.CreateAlgorithmVersionResponse: + """ + @summary 创建一个新的算法版本 + + @param request: CreateAlgorithmVersionRequest + @return: CreateAlgorithmVersionResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.create_algorithm_version_with_options(algorithm_id, algorithm_version, request, headers, runtime) @@ -558,6 +592,12 @@ async def create_algorithm_version_async( algorithm_version: str, request: pai_studio_20220112_models.CreateAlgorithmVersionRequest, ) -> pai_studio_20220112_models.CreateAlgorithmVersionResponse: + """ + @summary 创建一个新的算法版本 + + @param request: CreateAlgorithmVersionRequest + @return: CreateAlgorithmVersionResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.create_algorithm_version_with_options_async(algorithm_id, algorithm_version, request, headers, runtime) @@ -568,6 +608,14 @@ def create_component_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.CreateComponentResponse: + """ + @summary 创建组件 + + @param request: CreateComponentRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: CreateComponentResponse + """ UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.description): @@ -606,6 +654,14 @@ async def create_component_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.CreateComponentResponse: + """ + @summary 创建组件 + + @param request: CreateComponentRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: CreateComponentResponse + """ UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.description): @@ -642,6 +698,12 @@ def create_component( self, request: pai_studio_20220112_models.CreateComponentRequest, ) -> pai_studio_20220112_models.CreateComponentResponse: + """ + @summary 创建组件 + + @param request: CreateComponentRequest + @return: CreateComponentResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.create_component_with_options(request, headers, runtime) @@ -650,6 +712,12 @@ async def create_component_async( self, request: pai_studio_20220112_models.CreateComponentRequest, ) -> pai_studio_20220112_models.CreateComponentResponse: + """ + @summary 创建组件 + + @param request: CreateComponentRequest + @return: CreateComponentResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.create_component_with_options_async(request, headers, runtime) @@ -661,6 +729,14 @@ def create_component_version_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.CreateComponentVersionResponse: + """ + @summary 创建组件版本 + + @param request: CreateComponentVersionRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: CreateComponentVersionResponse + """ UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.config_dir): @@ -700,6 +776,14 @@ async def create_component_version_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.CreateComponentVersionResponse: + """ + @summary 创建组件版本 + + @param request: CreateComponentVersionRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: CreateComponentVersionResponse + """ UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.config_dir): @@ -737,6 +821,12 @@ def create_component_version( component_id: str, request: pai_studio_20220112_models.CreateComponentVersionRequest, ) -> pai_studio_20220112_models.CreateComponentVersionResponse: + """ + @summary 创建组件版本 + + @param request: CreateComponentVersionRequest + @return: CreateComponentVersionResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.create_component_version_with_options(component_id, request, headers, runtime) @@ -746,6 +836,12 @@ async def create_component_version_async( component_id: str, request: pai_studio_20220112_models.CreateComponentVersionRequest, ) -> pai_studio_20220112_models.CreateComponentVersionResponse: + """ + @summary 创建组件版本 + + @param request: CreateComponentVersionRequest + @return: CreateComponentVersionResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.create_component_version_with_options_async(component_id, request, headers, runtime) @@ -757,6 +853,13 @@ def create_instance_web_terminal_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.CreateInstanceWebTerminalResponse: + """ + @summary 创建WebTerminal + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: CreateInstanceWebTerminalResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -783,6 +886,13 @@ async def create_instance_web_terminal_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.CreateInstanceWebTerminalResponse: + """ + @summary 创建WebTerminal + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: CreateInstanceWebTerminalResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -807,6 +917,11 @@ def create_instance_web_terminal( training_job_id: str, instance_id: str, ) -> pai_studio_20220112_models.CreateInstanceWebTerminalResponse: + """ + @summary 创建WebTerminal + + @return: CreateInstanceWebTerminalResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.create_instance_web_terminal_with_options(training_job_id, instance_id, headers, runtime) @@ -816,272 +931,29 @@ async def create_instance_web_terminal_async( training_job_id: str, instance_id: str, ) -> pai_studio_20220112_models.CreateInstanceWebTerminalResponse: + """ + @summary 创建WebTerminal + + @return: CreateInstanceWebTerminalResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.create_instance_web_terminal_with_options_async(training_job_id, instance_id, headers, runtime) - def create_llmproject_with_options( - self, - request: pai_studio_20220112_models.CreateLLMProjectRequest, - headers: Dict[str, str], - runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.CreateLLMProjectResponse: - UtilClient.validate_model(request) - body = {} - if not UtilClient.is_unset(request.labels): - body['Labels'] = request.labels - if not UtilClient.is_unset(request.project_description): - body['ProjectDescription'] = request.project_description - if not UtilClient.is_unset(request.project_name): - body['ProjectName'] = request.project_name - if not UtilClient.is_unset(request.project_type): - body['ProjectType'] = request.project_type - if not UtilClient.is_unset(request.root_path): - body['RootPath'] = request.root_path - if not UtilClient.is_unset(request.runtime): - body['Runtime'] = request.runtime - if not UtilClient.is_unset(request.workspace_id): - body['WorkspaceId'] = request.workspace_id - req = open_api_models.OpenApiRequest( - headers=headers, - body=OpenApiUtilClient.parse_to_map(body) - ) - params = open_api_models.Params( - action='CreateLLMProject', - version='2022-01-12', - protocol='HTTPS', - pathname=f'/api/v1/langstudio/projects', - method='POST', - auth_type='AK', - style='ROA', - req_body_type='json', - body_type='json' - ) - return TeaCore.from_map( - pai_studio_20220112_models.CreateLLMProjectResponse(), - self.call_api(params, req, runtime) - ) - - async def create_llmproject_with_options_async( - self, - request: pai_studio_20220112_models.CreateLLMProjectRequest, - headers: Dict[str, str], - runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.CreateLLMProjectResponse: - UtilClient.validate_model(request) - body = {} - if not UtilClient.is_unset(request.labels): - body['Labels'] = request.labels - if not UtilClient.is_unset(request.project_description): - body['ProjectDescription'] = request.project_description - if not UtilClient.is_unset(request.project_name): - body['ProjectName'] = request.project_name - if not UtilClient.is_unset(request.project_type): - body['ProjectType'] = request.project_type - if not UtilClient.is_unset(request.root_path): - body['RootPath'] = request.root_path - if not UtilClient.is_unset(request.runtime): - body['Runtime'] = request.runtime - if not UtilClient.is_unset(request.workspace_id): - body['WorkspaceId'] = request.workspace_id - req = open_api_models.OpenApiRequest( - headers=headers, - body=OpenApiUtilClient.parse_to_map(body) - ) - params = open_api_models.Params( - action='CreateLLMProject', - version='2022-01-12', - protocol='HTTPS', - pathname=f'/api/v1/langstudio/projects', - method='POST', - auth_type='AK', - style='ROA', - req_body_type='json', - body_type='json' - ) - return TeaCore.from_map( - pai_studio_20220112_models.CreateLLMProjectResponse(), - await self.call_api_async(params, req, runtime) - ) - - def create_llmproject( - self, - request: pai_studio_20220112_models.CreateLLMProjectRequest, - ) -> pai_studio_20220112_models.CreateLLMProjectResponse: - runtime = util_models.RuntimeOptions() - headers = {} - return self.create_llmproject_with_options(request, headers, runtime) - - async def create_llmproject_async( - self, - request: pai_studio_20220112_models.CreateLLMProjectRequest, - ) -> pai_studio_20220112_models.CreateLLMProjectResponse: - runtime = util_models.RuntimeOptions() - headers = {} - return await self.create_llmproject_with_options_async(request, headers, runtime) - - def create_llmservice_identity_role_with_options( - self, - request: pai_studio_20220112_models.CreateLLMServiceIdentityRoleRequest, - headers: Dict[str, str], - runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.CreateLLMServiceIdentityRoleResponse: - UtilClient.validate_model(request) - body = {} - if not UtilClient.is_unset(request.role_name): - body['RoleName'] = request.role_name - req = open_api_models.OpenApiRequest( - headers=headers, - body=OpenApiUtilClient.parse_to_map(body) - ) - params = open_api_models.Params( - action='CreateLLMServiceIdentityRole', - version='2022-01-12', - protocol='HTTPS', - pathname=f'/api/v1/langstudio/serviceidentityroles', - method='POST', - auth_type='AK', - style='ROA', - req_body_type='json', - body_type='json' - ) - return TeaCore.from_map( - pai_studio_20220112_models.CreateLLMServiceIdentityRoleResponse(), - self.call_api(params, req, runtime) - ) - - async def create_llmservice_identity_role_with_options_async( - self, - request: pai_studio_20220112_models.CreateLLMServiceIdentityRoleRequest, - headers: Dict[str, str], - runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.CreateLLMServiceIdentityRoleResponse: - UtilClient.validate_model(request) - body = {} - if not UtilClient.is_unset(request.role_name): - body['RoleName'] = request.role_name - req = open_api_models.OpenApiRequest( - headers=headers, - body=OpenApiUtilClient.parse_to_map(body) - ) - params = open_api_models.Params( - action='CreateLLMServiceIdentityRole', - version='2022-01-12', - protocol='HTTPS', - pathname=f'/api/v1/langstudio/serviceidentityroles', - method='POST', - auth_type='AK', - style='ROA', - req_body_type='json', - body_type='json' - ) - return TeaCore.from_map( - pai_studio_20220112_models.CreateLLMServiceIdentityRoleResponse(), - await self.call_api_async(params, req, runtime) - ) - - def create_llmservice_identity_role( - self, - request: pai_studio_20220112_models.CreateLLMServiceIdentityRoleRequest, - ) -> pai_studio_20220112_models.CreateLLMServiceIdentityRoleResponse: - runtime = util_models.RuntimeOptions() - headers = {} - return self.create_llmservice_identity_role_with_options(request, headers, runtime) - - async def create_llmservice_identity_role_async( - self, - request: pai_studio_20220112_models.CreateLLMServiceIdentityRoleRequest, - ) -> pai_studio_20220112_models.CreateLLMServiceIdentityRoleResponse: - runtime = util_models.RuntimeOptions() - headers = {} - return await self.create_llmservice_identity_role_with_options_async(request, headers, runtime) - - def create_llmsnapshot_with_options( - self, - project_id: str, - request: pai_studio_20220112_models.CreateLLMSnapshotRequest, - headers: Dict[str, str], - runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.CreateLLMSnapshotResponse: - UtilClient.validate_model(request) - body = {} - if not UtilClient.is_unset(request.storage): - body['Storage'] = request.storage - req = open_api_models.OpenApiRequest( - headers=headers, - body=OpenApiUtilClient.parse_to_map(body) - ) - params = open_api_models.Params( - action='CreateLLMSnapshot', - version='2022-01-12', - protocol='HTTPS', - pathname=f'/api/v1/langstudio/projects/{OpenApiUtilClient.get_encode_param(project_id)}/snapshots', - method='POST', - auth_type='AK', - style='ROA', - req_body_type='json', - body_type='json' - ) - return TeaCore.from_map( - pai_studio_20220112_models.CreateLLMSnapshotResponse(), - self.call_api(params, req, runtime) - ) - - async def create_llmsnapshot_with_options_async( - self, - project_id: str, - request: pai_studio_20220112_models.CreateLLMSnapshotRequest, - headers: Dict[str, str], - runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.CreateLLMSnapshotResponse: - UtilClient.validate_model(request) - body = {} - if not UtilClient.is_unset(request.storage): - body['Storage'] = request.storage - req = open_api_models.OpenApiRequest( - headers=headers, - body=OpenApiUtilClient.parse_to_map(body) - ) - params = open_api_models.Params( - action='CreateLLMSnapshot', - version='2022-01-12', - protocol='HTTPS', - pathname=f'/api/v1/langstudio/projects/{OpenApiUtilClient.get_encode_param(project_id)}/snapshots', - method='POST', - auth_type='AK', - style='ROA', - req_body_type='json', - body_type='json' - ) - return TeaCore.from_map( - pai_studio_20220112_models.CreateLLMSnapshotResponse(), - await self.call_api_async(params, req, runtime) - ) - - def create_llmsnapshot( - self, - project_id: str, - request: pai_studio_20220112_models.CreateLLMSnapshotRequest, - ) -> pai_studio_20220112_models.CreateLLMSnapshotResponse: - runtime = util_models.RuntimeOptions() - headers = {} - return self.create_llmsnapshot_with_options(project_id, request, headers, runtime) - - async def create_llmsnapshot_async( - self, - project_id: str, - request: pai_studio_20220112_models.CreateLLMSnapshotRequest, - ) -> pai_studio_20220112_models.CreateLLMSnapshotResponse: - runtime = util_models.RuntimeOptions() - headers = {} - return await self.create_llmsnapshot_with_options_async(project_id, request, headers, runtime) - def create_quota_with_options( self, request: pai_studio_20220112_models.CreateQuotaRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.CreateQuotaResponse: + """ + @summary 创建Quota + + @param request: CreateQuotaRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: CreateQuotaResponse + """ UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.allocate_strategy): @@ -1130,6 +1002,14 @@ async def create_quota_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.CreateQuotaResponse: + """ + @summary 创建Quota + + @param request: CreateQuotaRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: CreateQuotaResponse + """ UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.allocate_strategy): @@ -1176,6 +1056,12 @@ def create_quota( self, request: pai_studio_20220112_models.CreateQuotaRequest, ) -> pai_studio_20220112_models.CreateQuotaResponse: + """ + @summary 创建Quota + + @param request: CreateQuotaRequest + @return: CreateQuotaResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.create_quota_with_options(request, headers, runtime) @@ -1184,6 +1070,12 @@ async def create_quota_async( self, request: pai_studio_20220112_models.CreateQuotaRequest, ) -> pai_studio_20220112_models.CreateQuotaResponse: + """ + @summary 创建Quota + + @param request: CreateQuotaRequest + @return: CreateQuotaResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.create_quota_with_options_async(request, headers, runtime) @@ -1194,6 +1086,14 @@ def create_resource_group_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.CreateResourceGroupResponse: + """ + @summary 创建资源组 + + @param request: CreateResourceGroupRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: CreateResourceGroupResponse + """ UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.computing_resource_provider): @@ -1234,6 +1134,14 @@ async def create_resource_group_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.CreateResourceGroupResponse: + """ + @summary 创建资源组 + + @param request: CreateResourceGroupRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: CreateResourceGroupResponse + """ UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.computing_resource_provider): @@ -1272,6 +1180,12 @@ def create_resource_group( self, request: pai_studio_20220112_models.CreateResourceGroupRequest, ) -> pai_studio_20220112_models.CreateResourceGroupResponse: + """ + @summary 创建资源组 + + @param request: CreateResourceGroupRequest + @return: CreateResourceGroupResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.create_resource_group_with_options(request, headers, runtime) @@ -1280,6 +1194,12 @@ async def create_resource_group_async( self, request: pai_studio_20220112_models.CreateResourceGroupRequest, ) -> pai_studio_20220112_models.CreateResourceGroupResponse: + """ + @summary 创建资源组 + + @param request: CreateResourceGroupRequest + @return: CreateResourceGroupResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.create_resource_group_with_options_async(request, headers, runtime) @@ -1291,6 +1211,14 @@ def create_resource_group_machine_group_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.CreateResourceGroupMachineGroupResponse: + """ + @summary 创建机器组 + + @param request: CreateResourceGroupMachineGroupRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: CreateResourceGroupMachineGroupResponse + """ UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.ecs_count): @@ -1334,6 +1262,14 @@ async def create_resource_group_machine_group_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.CreateResourceGroupMachineGroupResponse: + """ + @summary 创建机器组 + + @param request: CreateResourceGroupMachineGroupRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: CreateResourceGroupMachineGroupResponse + """ UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.ecs_count): @@ -1375,6 +1311,12 @@ def create_resource_group_machine_group( resource_group_id: str, request: pai_studio_20220112_models.CreateResourceGroupMachineGroupRequest, ) -> pai_studio_20220112_models.CreateResourceGroupMachineGroupResponse: + """ + @summary 创建机器组 + + @param request: CreateResourceGroupMachineGroupRequest + @return: CreateResourceGroupMachineGroupResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.create_resource_group_machine_group_with_options(resource_group_id, request, headers, runtime) @@ -1384,6 +1326,12 @@ async def create_resource_group_machine_group_async( resource_group_id: str, request: pai_studio_20220112_models.CreateResourceGroupMachineGroupRequest, ) -> pai_studio_20220112_models.CreateResourceGroupMachineGroupResponse: + """ + @summary 创建机器组 + + @param request: CreateResourceGroupMachineGroupRequest + @return: CreateResourceGroupMachineGroupResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.create_resource_group_machine_group_with_options_async(resource_group_id, request, headers, runtime) @@ -1394,6 +1342,14 @@ def create_service_identity_role_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.CreateServiceIdentityRoleResponse: + """ + @summary 创建服务认证角色 + + @param request: CreateServiceIdentityRoleRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: CreateServiceIdentityRoleResponse + """ UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.role_name): @@ -1424,6 +1380,14 @@ async def create_service_identity_role_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.CreateServiceIdentityRoleResponse: + """ + @summary 创建服务认证角色 + + @param request: CreateServiceIdentityRoleRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: CreateServiceIdentityRoleResponse + """ UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.role_name): @@ -1452,6 +1416,12 @@ def create_service_identity_role( self, request: pai_studio_20220112_models.CreateServiceIdentityRoleRequest, ) -> pai_studio_20220112_models.CreateServiceIdentityRoleResponse: + """ + @summary 创建服务认证角色 + + @param request: CreateServiceIdentityRoleRequest + @return: CreateServiceIdentityRoleResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.create_service_identity_role_with_options(request, headers, runtime) @@ -1460,6 +1430,12 @@ async def create_service_identity_role_async( self, request: pai_studio_20220112_models.CreateServiceIdentityRoleRequest, ) -> pai_studio_20220112_models.CreateServiceIdentityRoleResponse: + """ + @summary 创建服务认证角色 + + @param request: CreateServiceIdentityRoleRequest + @return: CreateServiceIdentityRoleResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.create_service_identity_role_with_options_async(request, headers, runtime) @@ -1470,6 +1446,14 @@ def create_training_job_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.CreateTrainingJobResponse: + """ + @summary 创建TrainingJob + + @param request: CreateTrainingJobRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: CreateTrainingJobResponse + """ UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.algorithm_name): @@ -1538,6 +1522,14 @@ async def create_training_job_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.CreateTrainingJobResponse: + """ + @summary 创建TrainingJob + + @param request: CreateTrainingJobRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: CreateTrainingJobResponse + """ UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.algorithm_name): @@ -1604,6 +1596,12 @@ def create_training_job( self, request: pai_studio_20220112_models.CreateTrainingJobRequest, ) -> pai_studio_20220112_models.CreateTrainingJobResponse: + """ + @summary 创建TrainingJob + + @param request: CreateTrainingJobRequest + @return: CreateTrainingJobResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.create_training_job_with_options(request, headers, runtime) @@ -1612,6 +1610,12 @@ async def create_training_job_async( self, request: pai_studio_20220112_models.CreateTrainingJobRequest, ) -> pai_studio_20220112_models.CreateTrainingJobResponse: + """ + @summary 创建TrainingJob + + @param request: CreateTrainingJobRequest + @return: CreateTrainingJobResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.create_training_job_with_options_async(request, headers, runtime) @@ -1622,6 +1626,13 @@ def delete_algorithm_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.DeleteAlgorithmResponse: + """ + @summary 删除算法 + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: DeleteAlgorithmResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -1647,6 +1658,13 @@ async def delete_algorithm_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.DeleteAlgorithmResponse: + """ + @summary 删除算法 + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: DeleteAlgorithmResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -1670,6 +1688,11 @@ def delete_algorithm( self, algorithm_id: str, ) -> pai_studio_20220112_models.DeleteAlgorithmResponse: + """ + @summary 删除算法 + + @return: DeleteAlgorithmResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.delete_algorithm_with_options(algorithm_id, headers, runtime) @@ -1678,6 +1701,11 @@ async def delete_algorithm_async( self, algorithm_id: str, ) -> pai_studio_20220112_models.DeleteAlgorithmResponse: + """ + @summary 删除算法 + + @return: DeleteAlgorithmResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.delete_algorithm_with_options_async(algorithm_id, headers, runtime) @@ -1689,6 +1717,13 @@ def delete_algorithm_version_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.DeleteAlgorithmVersionResponse: + """ + @summary 删除算法版本 + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: DeleteAlgorithmVersionResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -1715,6 +1750,13 @@ async def delete_algorithm_version_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.DeleteAlgorithmVersionResponse: + """ + @summary 删除算法版本 + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: DeleteAlgorithmVersionResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -1739,6 +1781,11 @@ def delete_algorithm_version( algorithm_id: str, algorithm_version: str, ) -> pai_studio_20220112_models.DeleteAlgorithmVersionResponse: + """ + @summary 删除算法版本 + + @return: DeleteAlgorithmVersionResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.delete_algorithm_version_with_options(algorithm_id, algorithm_version, headers, runtime) @@ -1748,6 +1795,11 @@ async def delete_algorithm_version_async( algorithm_id: str, algorithm_version: str, ) -> pai_studio_20220112_models.DeleteAlgorithmVersionResponse: + """ + @summary 删除算法版本 + + @return: DeleteAlgorithmVersionResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.delete_algorithm_version_with_options_async(algorithm_id, algorithm_version, headers, runtime) @@ -1758,6 +1810,13 @@ def delete_component_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.DeleteComponentResponse: + """ + @summary 删除组件 + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: DeleteComponentResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -1783,6 +1842,13 @@ async def delete_component_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.DeleteComponentResponse: + """ + @summary 删除组件 + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: DeleteComponentResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -1806,6 +1872,11 @@ def delete_component( self, component_id: str, ) -> pai_studio_20220112_models.DeleteComponentResponse: + """ + @summary 删除组件 + + @return: DeleteComponentResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.delete_component_with_options(component_id, headers, runtime) @@ -1814,6 +1885,11 @@ async def delete_component_async( self, component_id: str, ) -> pai_studio_20220112_models.DeleteComponentResponse: + """ + @summary 删除组件 + + @return: DeleteComponentResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.delete_component_with_options_async(component_id, headers, runtime) @@ -1825,6 +1901,13 @@ def delete_component_version_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.DeleteComponentVersionResponse: + """ + @summary 删除组件版本 + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: DeleteComponentVersionResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -1851,6 +1934,13 @@ async def delete_component_version_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.DeleteComponentVersionResponse: + """ + @summary 删除组件版本 + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: DeleteComponentVersionResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -1875,6 +1965,11 @@ def delete_component_version( component_id: str, version: str, ) -> pai_studio_20220112_models.DeleteComponentVersionResponse: + """ + @summary 删除组件版本 + + @return: DeleteComponentVersionResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.delete_component_version_with_options(component_id, version, headers, runtime) @@ -1884,6 +1979,11 @@ async def delete_component_version_async( component_id: str, version: str, ) -> pai_studio_20220112_models.DeleteComponentVersionResponse: + """ + @summary 删除组件版本 + + @return: DeleteComponentVersionResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.delete_component_version_with_options_async(component_id, version, headers, runtime) @@ -1894,6 +1994,13 @@ def delete_component_version_snapshot_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.DeleteComponentVersionSnapshotResponse: + """ + @summary 删除组件版本快照 + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: DeleteComponentVersionSnapshotResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -1919,6 +2026,13 @@ async def delete_component_version_snapshot_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.DeleteComponentVersionSnapshotResponse: + """ + @summary 删除组件版本快照 + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: DeleteComponentVersionSnapshotResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -1942,6 +2056,11 @@ def delete_component_version_snapshot( self, snapshot_id: str, ) -> pai_studio_20220112_models.DeleteComponentVersionSnapshotResponse: + """ + @summary 删除组件版本快照 + + @return: DeleteComponentVersionSnapshotResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.delete_component_version_snapshot_with_options(snapshot_id, headers, runtime) @@ -1950,24 +2069,36 @@ async def delete_component_version_snapshot_async( self, snapshot_id: str, ) -> pai_studio_20220112_models.DeleteComponentVersionSnapshotResponse: + """ + @summary 删除组件版本快照 + + @return: DeleteComponentVersionSnapshotResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.delete_component_version_snapshot_with_options_async(snapshot_id, headers, runtime) - def delete_llmproject_with_options( + def delete_machine_group_with_options( self, - project_id: str, + machine_group_id: str, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.DeleteLLMProjectResponse: + ) -> pai_studio_20220112_models.DeleteMachineGroupResponse: + """ + @summary delete machine group + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: DeleteMachineGroupResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) params = open_api_models.Params( - action='DeleteLLMProject', + action='DeleteMachineGroup', version='2022-01-12', protocol='HTTPS', - pathname=f'/api/v1/langstudio/projects/{OpenApiUtilClient.get_encode_param(project_id)}', + pathname=f'/api/v1/resources/machinegroups/{OpenApiUtilClient.get_encode_param(machine_group_id)}', method='DELETE', auth_type='AK', style='ROA', @@ -1975,24 +2106,31 @@ def delete_llmproject_with_options( body_type='json' ) return TeaCore.from_map( - pai_studio_20220112_models.DeleteLLMProjectResponse(), + pai_studio_20220112_models.DeleteMachineGroupResponse(), self.call_api(params, req, runtime) ) - async def delete_llmproject_with_options_async( + async def delete_machine_group_with_options_async( self, - project_id: str, + machine_group_id: str, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.DeleteLLMProjectResponse: + ) -> pai_studio_20220112_models.DeleteMachineGroupResponse: + """ + @summary delete machine group + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: DeleteMachineGroupResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) params = open_api_models.Params( - action='DeleteLLMProject', + action='DeleteMachineGroup', version='2022-01-12', protocol='HTTPS', - pathname=f'/api/v1/langstudio/projects/{OpenApiUtilClient.get_encode_param(project_id)}', + pathname=f'/api/v1/resources/machinegroups/{OpenApiUtilClient.get_encode_param(machine_group_id)}', method='DELETE', auth_type='AK', style='ROA', @@ -2000,88 +2138,32 @@ async def delete_llmproject_with_options_async( body_type='json' ) return TeaCore.from_map( - pai_studio_20220112_models.DeleteLLMProjectResponse(), + pai_studio_20220112_models.DeleteMachineGroupResponse(), await self.call_api_async(params, req, runtime) ) - def delete_llmproject( + def delete_machine_group( self, - project_id: str, - ) -> pai_studio_20220112_models.DeleteLLMProjectResponse: + machine_group_id: str, + ) -> pai_studio_20220112_models.DeleteMachineGroupResponse: + """ + @summary delete machine group + + @return: DeleteMachineGroupResponse + """ runtime = util_models.RuntimeOptions() headers = {} - return self.delete_llmproject_with_options(project_id, headers, runtime) + return self.delete_machine_group_with_options(machine_group_id, headers, runtime) - async def delete_llmproject_async( + async def delete_machine_group_async( self, - project_id: str, - ) -> pai_studio_20220112_models.DeleteLLMProjectResponse: - runtime = util_models.RuntimeOptions() - headers = {} - return await self.delete_llmproject_with_options_async(project_id, headers, runtime) - - def delete_machine_group_with_options( - self, - machine_group_id: str, - headers: Dict[str, str], - runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.DeleteMachineGroupResponse: - req = open_api_models.OpenApiRequest( - headers=headers - ) - params = open_api_models.Params( - action='DeleteMachineGroup', - version='2022-01-12', - protocol='HTTPS', - pathname=f'/api/v1/resources/machinegroups/{OpenApiUtilClient.get_encode_param(machine_group_id)}', - method='DELETE', - auth_type='AK', - style='ROA', - req_body_type='json', - body_type='json' - ) - return TeaCore.from_map( - pai_studio_20220112_models.DeleteMachineGroupResponse(), - self.call_api(params, req, runtime) - ) - - async def delete_machine_group_with_options_async( - self, - machine_group_id: str, - headers: Dict[str, str], - runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.DeleteMachineGroupResponse: - req = open_api_models.OpenApiRequest( - headers=headers - ) - params = open_api_models.Params( - action='DeleteMachineGroup', - version='2022-01-12', - protocol='HTTPS', - pathname=f'/api/v1/resources/machinegroups/{OpenApiUtilClient.get_encode_param(machine_group_id)}', - method='DELETE', - auth_type='AK', - style='ROA', - req_body_type='json', - body_type='json' - ) - return TeaCore.from_map( - pai_studio_20220112_models.DeleteMachineGroupResponse(), - await self.call_api_async(params, req, runtime) - ) - - def delete_machine_group( - self, - machine_group_id: str, - ) -> pai_studio_20220112_models.DeleteMachineGroupResponse: - runtime = util_models.RuntimeOptions() - headers = {} - return self.delete_machine_group_with_options(machine_group_id, headers, runtime) - - async def delete_machine_group_async( - self, - machine_group_id: str, - ) -> pai_studio_20220112_models.DeleteMachineGroupResponse: + machine_group_id: str, + ) -> pai_studio_20220112_models.DeleteMachineGroupResponse: + """ + @summary delete machine group + + @return: DeleteMachineGroupResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.delete_machine_group_with_options_async(machine_group_id, headers, runtime) @@ -2092,6 +2174,13 @@ def delete_quota_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.DeleteQuotaResponse: + """ + @summary 删除Quota + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: DeleteQuotaResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -2117,6 +2206,13 @@ async def delete_quota_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.DeleteQuotaResponse: + """ + @summary 删除Quota + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: DeleteQuotaResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -2140,6 +2236,11 @@ def delete_quota( self, quota_id: str, ) -> pai_studio_20220112_models.DeleteQuotaResponse: + """ + @summary 删除Quota + + @return: DeleteQuotaResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.delete_quota_with_options(quota_id, headers, runtime) @@ -2148,6 +2249,11 @@ async def delete_quota_async( self, quota_id: str, ) -> pai_studio_20220112_models.DeleteQuotaResponse: + """ + @summary 删除Quota + + @return: DeleteQuotaResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.delete_quota_with_options_async(quota_id, headers, runtime) @@ -2159,6 +2265,14 @@ def delete_quota_labels_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.DeleteQuotaLabelsResponse: + """ + @summary 删除Quota标签 + + @param request: DeleteQuotaLabelsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: DeleteQuotaLabelsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.keys): @@ -2190,6 +2304,14 @@ async def delete_quota_labels_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.DeleteQuotaLabelsResponse: + """ + @summary 删除Quota标签 + + @param request: DeleteQuotaLabelsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: DeleteQuotaLabelsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.keys): @@ -2219,6 +2341,12 @@ def delete_quota_labels( quota_id: str, request: pai_studio_20220112_models.DeleteQuotaLabelsRequest, ) -> pai_studio_20220112_models.DeleteQuotaLabelsResponse: + """ + @summary 删除Quota标签 + + @param request: DeleteQuotaLabelsRequest + @return: DeleteQuotaLabelsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.delete_quota_labels_with_options(quota_id, request, headers, runtime) @@ -2228,6 +2356,12 @@ async def delete_quota_labels_async( quota_id: str, request: pai_studio_20220112_models.DeleteQuotaLabelsRequest, ) -> pai_studio_20220112_models.DeleteQuotaLabelsResponse: + """ + @summary 删除Quota标签 + + @param request: DeleteQuotaLabelsRequest + @return: DeleteQuotaLabelsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.delete_quota_labels_with_options_async(quota_id, request, headers, runtime) @@ -2238,6 +2372,13 @@ def delete_resource_group_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.DeleteResourceGroupResponse: + """ + @summary 删除资源组 + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: DeleteResourceGroupResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -2263,6 +2404,13 @@ async def delete_resource_group_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.DeleteResourceGroupResponse: + """ + @summary 删除资源组 + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: DeleteResourceGroupResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -2286,6 +2434,11 @@ def delete_resource_group( self, resource_group_id: str, ) -> pai_studio_20220112_models.DeleteResourceGroupResponse: + """ + @summary 删除资源组 + + @return: DeleteResourceGroupResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.delete_resource_group_with_options(resource_group_id, headers, runtime) @@ -2294,6 +2447,11 @@ async def delete_resource_group_async( self, resource_group_id: str, ) -> pai_studio_20220112_models.DeleteResourceGroupResponse: + """ + @summary 删除资源组 + + @return: DeleteResourceGroupResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.delete_resource_group_with_options_async(resource_group_id, headers, runtime) @@ -2305,6 +2463,13 @@ def delete_resource_group_machine_group_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.DeleteResourceGroupMachineGroupResponse: + """ + @summary delete machine group + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: DeleteResourceGroupMachineGroupResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -2331,6 +2496,13 @@ async def delete_resource_group_machine_group_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.DeleteResourceGroupMachineGroupResponse: + """ + @summary delete machine group + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: DeleteResourceGroupMachineGroupResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -2355,6 +2527,11 @@ def delete_resource_group_machine_group( machine_group_id: str, resource_group_id: str, ) -> pai_studio_20220112_models.DeleteResourceGroupMachineGroupResponse: + """ + @summary delete machine group + + @return: DeleteResourceGroupMachineGroupResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.delete_resource_group_machine_group_with_options(machine_group_id, resource_group_id, headers, runtime) @@ -2364,6 +2541,11 @@ async def delete_resource_group_machine_group_async( machine_group_id: str, resource_group_id: str, ) -> pai_studio_20220112_models.DeleteResourceGroupMachineGroupResponse: + """ + @summary delete machine group + + @return: DeleteResourceGroupMachineGroupResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.delete_resource_group_machine_group_with_options_async(machine_group_id, resource_group_id, headers, runtime) @@ -2374,6 +2556,13 @@ def delete_training_job_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.DeleteTrainingJobResponse: + """ + @summary 删除一个TrainingJob + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: DeleteTrainingJobResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -2399,6 +2588,13 @@ async def delete_training_job_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.DeleteTrainingJobResponse: + """ + @summary 删除一个TrainingJob + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: DeleteTrainingJobResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -2422,6 +2618,11 @@ def delete_training_job( self, training_job_id: str, ) -> pai_studio_20220112_models.DeleteTrainingJobResponse: + """ + @summary 删除一个TrainingJob + + @return: DeleteTrainingJobResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.delete_training_job_with_options(training_job_id, headers, runtime) @@ -2430,6 +2631,11 @@ async def delete_training_job_async( self, training_job_id: str, ) -> pai_studio_20220112_models.DeleteTrainingJobResponse: + """ + @summary 删除一个TrainingJob + + @return: DeleteTrainingJobResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.delete_training_job_with_options_async(training_job_id, headers, runtime) @@ -2441,6 +2647,14 @@ def delete_training_job_labels_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.DeleteTrainingJobLabelsResponse: + """ + @summary 删除TrainingJob的Labels + + @param request: DeleteTrainingJobLabelsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: DeleteTrainingJobLabelsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.keys): @@ -2472,6 +2686,14 @@ async def delete_training_job_labels_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.DeleteTrainingJobLabelsResponse: + """ + @summary 删除TrainingJob的Labels + + @param request: DeleteTrainingJobLabelsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: DeleteTrainingJobLabelsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.keys): @@ -2501,6 +2723,12 @@ def delete_training_job_labels( training_job_id: str, request: pai_studio_20220112_models.DeleteTrainingJobLabelsRequest, ) -> pai_studio_20220112_models.DeleteTrainingJobLabelsResponse: + """ + @summary 删除TrainingJob的Labels + + @param request: DeleteTrainingJobLabelsRequest + @return: DeleteTrainingJobLabelsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.delete_training_job_labels_with_options(training_job_id, request, headers, runtime) @@ -2510,111 +2738,28 @@ async def delete_training_job_labels_async( training_job_id: str, request: pai_studio_20220112_models.DeleteTrainingJobLabelsRequest, ) -> pai_studio_20220112_models.DeleteTrainingJobLabelsResponse: + """ + @summary 删除TrainingJob的Labels + + @param request: DeleteTrainingJobLabelsRequest + @return: DeleteTrainingJobLabelsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.delete_training_job_labels_with_options_async(training_job_id, request, headers, runtime) - def deploy_llmsnapshot_with_options( - self, - project_id: str, - snapshot_id: str, - request: pai_studio_20220112_models.DeployLLMSnapshotRequest, - headers: Dict[str, str], - runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.DeployLLMSnapshotResponse: - UtilClient.validate_model(request) - body = {} - if not UtilClient.is_unset(request.description): - body['Description'] = request.description - if not UtilClient.is_unset(request.display_name): - body['DisplayName'] = request.display_name - if not UtilClient.is_unset(request.labels): - body['Labels'] = request.labels - if not UtilClient.is_unset(request.workload): - body['Workload'] = request.workload - req = open_api_models.OpenApiRequest( - headers=headers, - body=OpenApiUtilClient.parse_to_map(body) - ) - params = open_api_models.Params( - action='DeployLLMSnapshot', - version='2022-01-12', - protocol='HTTPS', - pathname=f'/api/v1/langstudio/projects/{OpenApiUtilClient.get_encode_param(project_id)}/snapshots/{OpenApiUtilClient.get_encode_param(snapshot_id)}/deploy', - method='PUT', - auth_type='AK', - style='ROA', - req_body_type='json', - body_type='json' - ) - return TeaCore.from_map( - pai_studio_20220112_models.DeployLLMSnapshotResponse(), - self.call_api(params, req, runtime) - ) - - async def deploy_llmsnapshot_with_options_async( - self, - project_id: str, - snapshot_id: str, - request: pai_studio_20220112_models.DeployLLMSnapshotRequest, - headers: Dict[str, str], - runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.DeployLLMSnapshotResponse: - UtilClient.validate_model(request) - body = {} - if not UtilClient.is_unset(request.description): - body['Description'] = request.description - if not UtilClient.is_unset(request.display_name): - body['DisplayName'] = request.display_name - if not UtilClient.is_unset(request.labels): - body['Labels'] = request.labels - if not UtilClient.is_unset(request.workload): - body['Workload'] = request.workload - req = open_api_models.OpenApiRequest( - headers=headers, - body=OpenApiUtilClient.parse_to_map(body) - ) - params = open_api_models.Params( - action='DeployLLMSnapshot', - version='2022-01-12', - protocol='HTTPS', - pathname=f'/api/v1/langstudio/projects/{OpenApiUtilClient.get_encode_param(project_id)}/snapshots/{OpenApiUtilClient.get_encode_param(snapshot_id)}/deploy', - method='PUT', - auth_type='AK', - style='ROA', - req_body_type='json', - body_type='json' - ) - return TeaCore.from_map( - pai_studio_20220112_models.DeployLLMSnapshotResponse(), - await self.call_api_async(params, req, runtime) - ) - - def deploy_llmsnapshot( - self, - project_id: str, - snapshot_id: str, - request: pai_studio_20220112_models.DeployLLMSnapshotRequest, - ) -> pai_studio_20220112_models.DeployLLMSnapshotResponse: - runtime = util_models.RuntimeOptions() - headers = {} - return self.deploy_llmsnapshot_with_options(project_id, snapshot_id, request, headers, runtime) - - async def deploy_llmsnapshot_async( - self, - project_id: str, - snapshot_id: str, - request: pai_studio_20220112_models.DeployLLMSnapshotRequest, - ) -> pai_studio_20220112_models.DeployLLMSnapshotResponse: - runtime = util_models.RuntimeOptions() - headers = {} - return await self.deploy_llmsnapshot_with_options_async(project_id, snapshot_id, request, headers, runtime) - def get_ai4ddefault_bucket_with_options( self, headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetAI4DDefaultBucketResponse: + """ + @summary 获取AI4D模型桶 + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetAI4DDefaultBucketResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -2639,6 +2784,13 @@ async def get_ai4ddefault_bucket_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetAI4DDefaultBucketResponse: + """ + @summary 获取AI4D模型桶 + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetAI4DDefaultBucketResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -2659,11 +2811,21 @@ async def get_ai4ddefault_bucket_with_options_async( ) def get_ai4ddefault_bucket(self) -> pai_studio_20220112_models.GetAI4DDefaultBucketResponse: + """ + @summary 获取AI4D模型桶 + + @return: GetAI4DDefaultBucketResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.get_ai4ddefault_bucket_with_options(headers, runtime) async def get_ai4ddefault_bucket_async(self) -> pai_studio_20220112_models.GetAI4DDefaultBucketResponse: + """ + @summary 获取AI4D模型桶 + + @return: GetAI4DDefaultBucketResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.get_ai4ddefault_bucket_with_options_async(headers, runtime) @@ -2674,6 +2836,13 @@ def get_algorithm_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetAlgorithmResponse: + """ + @summary 获取一个算法信息 + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetAlgorithmResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -2699,6 +2868,13 @@ async def get_algorithm_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetAlgorithmResponse: + """ + @summary 获取一个算法信息 + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetAlgorithmResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -2722,6 +2898,11 @@ def get_algorithm( self, algorithm_id: str, ) -> pai_studio_20220112_models.GetAlgorithmResponse: + """ + @summary 获取一个算法信息 + + @return: GetAlgorithmResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.get_algorithm_with_options(algorithm_id, headers, runtime) @@ -2730,6 +2911,11 @@ async def get_algorithm_async( self, algorithm_id: str, ) -> pai_studio_20220112_models.GetAlgorithmResponse: + """ + @summary 获取一个算法信息 + + @return: GetAlgorithmResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.get_algorithm_with_options_async(algorithm_id, headers, runtime) @@ -2741,6 +2927,13 @@ def get_algorithm_version_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetAlgorithmVersionResponse: + """ + @summary 创建一个新的算法版本 + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetAlgorithmVersionResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -2767,6 +2960,13 @@ async def get_algorithm_version_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetAlgorithmVersionResponse: + """ + @summary 创建一个新的算法版本 + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetAlgorithmVersionResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -2791,6 +2991,11 @@ def get_algorithm_version( algorithm_id: str, algorithm_version: str, ) -> pai_studio_20220112_models.GetAlgorithmVersionResponse: + """ + @summary 创建一个新的算法版本 + + @return: GetAlgorithmVersionResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.get_algorithm_version_with_options(algorithm_id, algorithm_version, headers, runtime) @@ -2800,6 +3005,11 @@ async def get_algorithm_version_async( algorithm_id: str, algorithm_version: str, ) -> pai_studio_20220112_models.GetAlgorithmVersionResponse: + """ + @summary 创建一个新的算法版本 + + @return: GetAlgorithmVersionResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.get_algorithm_version_with_options_async(algorithm_id, algorithm_version, headers, runtime) @@ -2810,6 +3020,13 @@ def get_component_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetComponentResponse: + """ + @summary 查询组件信息 + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetComponentResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -2835,6 +3052,13 @@ async def get_component_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetComponentResponse: + """ + @summary 查询组件信息 + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetComponentResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -2858,6 +3082,11 @@ def get_component( self, component_id: str, ) -> pai_studio_20220112_models.GetComponentResponse: + """ + @summary 查询组件信息 + + @return: GetComponentResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.get_component_with_options(component_id, headers, runtime) @@ -2866,6 +3095,11 @@ async def get_component_async( self, component_id: str, ) -> pai_studio_20220112_models.GetComponentResponse: + """ + @summary 查询组件信息 + + @return: GetComponentResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.get_component_with_options_async(component_id, headers, runtime) @@ -2877,6 +3111,13 @@ def get_component_version_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetComponentVersionResponse: + """ + @summary 获取组件版本 + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetComponentVersionResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -2903,6 +3144,13 @@ async def get_component_version_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetComponentVersionResponse: + """ + @summary 获取组件版本 + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetComponentVersionResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -2927,6 +3175,11 @@ def get_component_version( component_id: str, version: str, ) -> pai_studio_20220112_models.GetComponentVersionResponse: + """ + @summary 获取组件版本 + + @return: GetComponentVersionResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.get_component_version_with_options(component_id, version, headers, runtime) @@ -2936,6 +3189,11 @@ async def get_component_version_async( component_id: str, version: str, ) -> pai_studio_20220112_models.GetComponentVersionResponse: + """ + @summary 获取组件版本 + + @return: GetComponentVersionResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.get_component_version_with_options_async(component_id, version, headers, runtime) @@ -2946,6 +3204,13 @@ def get_component_version_snapshot_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetComponentVersionSnapshotResponse: + """ + @summary 获取组件版本快照 + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetComponentVersionSnapshotResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -2971,6 +3236,13 @@ async def get_component_version_snapshot_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetComponentVersionSnapshotResponse: + """ + @summary 获取组件版本快照 + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetComponentVersionSnapshotResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -2994,6 +3266,11 @@ def get_component_version_snapshot( self, snapshot_id: str, ) -> pai_studio_20220112_models.GetComponentVersionSnapshotResponse: + """ + @summary 获取组件版本快照 + + @return: GetComponentVersionSnapshotResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.get_component_version_snapshot_with_options(snapshot_id, headers, runtime) @@ -3002,6 +3279,11 @@ async def get_component_version_snapshot_async( self, snapshot_id: str, ) -> pai_studio_20220112_models.GetComponentVersionSnapshotResponse: + """ + @summary 获取组件版本快照 + + @return: GetComponentVersionSnapshotResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.get_component_version_snapshot_with_options_async(snapshot_id, headers, runtime) @@ -3012,6 +3294,13 @@ def get_instance_job_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetInstanceJobResponse: + """ + @summary 获取实例任务 + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetInstanceJobResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -3037,6 +3326,13 @@ async def get_instance_job_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetInstanceJobResponse: + """ + @summary 获取实例任务 + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetInstanceJobResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -3060,6 +3356,11 @@ def get_instance_job( self, instance_job_id: str, ) -> pai_studio_20220112_models.GetInstanceJobResponse: + """ + @summary 获取实例任务 + + @return: GetInstanceJobResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.get_instance_job_with_options(instance_job_id, headers, runtime) @@ -3068,6 +3369,11 @@ async def get_instance_job_async( self, instance_job_id: str, ) -> pai_studio_20220112_models.GetInstanceJobResponse: + """ + @summary 获取实例任务 + + @return: GetInstanceJobResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.get_instance_job_with_options_async(instance_job_id, headers, runtime) @@ -3079,6 +3385,14 @@ def get_job_view_metrics_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetJobViewMetricsResponse: + """ + @summary 按照job来统计性能指标 + + @param request: GetJobViewMetricsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetJobViewMetricsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.end_time): @@ -3122,6 +3436,14 @@ async def get_job_view_metrics_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetJobViewMetricsResponse: + """ + @summary 按照job来统计性能指标 + + @param request: GetJobViewMetricsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetJobViewMetricsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.end_time): @@ -3163,6 +3485,12 @@ def get_job_view_metrics( resource_group_id: str, request: pai_studio_20220112_models.GetJobViewMetricsRequest, ) -> pai_studio_20220112_models.GetJobViewMetricsResponse: + """ + @summary 按照job来统计性能指标 + + @param request: GetJobViewMetricsRequest + @return: GetJobViewMetricsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.get_job_view_metrics_with_options(resource_group_id, request, headers, runtime) @@ -3172,6 +3500,12 @@ async def get_job_view_metrics_async( resource_group_id: str, request: pai_studio_20220112_models.GetJobViewMetricsRequest, ) -> pai_studio_20220112_models.GetJobViewMetricsResponse: + """ + @summary 按照job来统计性能指标 + + @param request: GetJobViewMetricsRequest + @return: GetJobViewMetricsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.get_job_view_metrics_with_options_async(resource_group_id, request, headers, runtime) @@ -3183,6 +3517,14 @@ def get_jobs_statistics_by_quota_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetJobsStatisticsByQuotaResponse: + """ + @summary 获取当前资源配额的作业统计信息 + + @param request: GetJobsStatisticsByQuotaRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetJobsStatisticsByQuotaResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.end_time): @@ -3218,6 +3560,14 @@ async def get_jobs_statistics_by_quota_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetJobsStatisticsByQuotaResponse: + """ + @summary 获取当前资源配额的作业统计信息 + + @param request: GetJobsStatisticsByQuotaRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetJobsStatisticsByQuotaResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.end_time): @@ -3251,6 +3601,12 @@ def get_jobs_statistics_by_quota( quota_id: str, request: pai_studio_20220112_models.GetJobsStatisticsByQuotaRequest, ) -> pai_studio_20220112_models.GetJobsStatisticsByQuotaResponse: + """ + @summary 获取当前资源配额的作业统计信息 + + @param request: GetJobsStatisticsByQuotaRequest + @return: GetJobsStatisticsByQuotaResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.get_jobs_statistics_by_quota_with_options(quota_id, request, headers, runtime) @@ -3260,6 +3616,12 @@ async def get_jobs_statistics_by_quota_async( quota_id: str, request: pai_studio_20220112_models.GetJobsStatisticsByQuotaRequest, ) -> pai_studio_20220112_models.GetJobsStatisticsByQuotaResponse: + """ + @summary 获取当前资源配额的作业统计信息 + + @param request: GetJobsStatisticsByQuotaRequest + @return: GetJobsStatisticsByQuotaResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.get_jobs_statistics_by_quota_with_options_async(quota_id, request, headers, runtime) @@ -3271,6 +3633,14 @@ def get_jobs_statistics_by_resource_group_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetJobsStatisticsByResourceGroupResponse: + """ + @summary 按照resource group,查询Job的状态统计信息 + + @param request: GetJobsStatisticsByResourceGroupRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetJobsStatisticsByResourceGroupResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.end_time): @@ -3306,6 +3676,14 @@ async def get_jobs_statistics_by_resource_group_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetJobsStatisticsByResourceGroupResponse: + """ + @summary 按照resource group,查询Job的状态统计信息 + + @param request: GetJobsStatisticsByResourceGroupRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetJobsStatisticsByResourceGroupResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.end_time): @@ -3339,6 +3717,12 @@ def get_jobs_statistics_by_resource_group( resource_group_id: str, request: pai_studio_20220112_models.GetJobsStatisticsByResourceGroupRequest, ) -> pai_studio_20220112_models.GetJobsStatisticsByResourceGroupResponse: + """ + @summary 按照resource group,查询Job的状态统计信息 + + @param request: GetJobsStatisticsByResourceGroupRequest + @return: GetJobsStatisticsByResourceGroupResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.get_jobs_statistics_by_resource_group_with_options(resource_group_id, request, headers, runtime) @@ -3348,24 +3732,37 @@ async def get_jobs_statistics_by_resource_group_async( resource_group_id: str, request: pai_studio_20220112_models.GetJobsStatisticsByResourceGroupRequest, ) -> pai_studio_20220112_models.GetJobsStatisticsByResourceGroupResponse: + """ + @summary 按照resource group,查询Job的状态统计信息 + + @param request: GetJobsStatisticsByResourceGroupRequest + @return: GetJobsStatisticsByResourceGroupResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.get_jobs_statistics_by_resource_group_with_options_async(resource_group_id, request, headers, runtime) - def get_llmproject_with_options( + def get_machine_group_with_options( self, - project_id: str, + machine_group_id: str, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.GetLLMProjectResponse: + ) -> pai_studio_20220112_models.GetMachineGroupResponse: + """ + @summary get machine group + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetMachineGroupResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) params = open_api_models.Params( - action='GetLLMProject', + action='GetMachineGroup', version='2022-01-12', protocol='HTTPS', - pathname=f'/api/v1/langstudio/projects/{OpenApiUtilClient.get_encode_param(project_id)}', + pathname=f'/api/v1/resources/machinegroups/{OpenApiUtilClient.get_encode_param(machine_group_id)}', method='GET', auth_type='AK', style='ROA', @@ -3373,24 +3770,31 @@ def get_llmproject_with_options( body_type='json' ) return TeaCore.from_map( - pai_studio_20220112_models.GetLLMProjectResponse(), + pai_studio_20220112_models.GetMachineGroupResponse(), self.call_api(params, req, runtime) ) - async def get_llmproject_with_options_async( + async def get_machine_group_with_options_async( self, - project_id: str, + machine_group_id: str, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.GetLLMProjectResponse: + ) -> pai_studio_20220112_models.GetMachineGroupResponse: + """ + @summary get machine group + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetMachineGroupResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) params = open_api_models.Params( - action='GetLLMProject', + action='GetMachineGroup', version='2022-01-12', protocol='HTTPS', - pathname=f'/api/v1/langstudio/projects/{OpenApiUtilClient.get_encode_param(project_id)}', + pathname=f'/api/v1/resources/machinegroups/{OpenApiUtilClient.get_encode_param(machine_group_id)}', method='GET', auth_type='AK', style='ROA', @@ -3398,40 +3802,79 @@ async def get_llmproject_with_options_async( body_type='json' ) return TeaCore.from_map( - pai_studio_20220112_models.GetLLMProjectResponse(), + pai_studio_20220112_models.GetMachineGroupResponse(), await self.call_api_async(params, req, runtime) ) - def get_llmproject( + def get_machine_group( self, - project_id: str, - ) -> pai_studio_20220112_models.GetLLMProjectResponse: + machine_group_id: str, + ) -> pai_studio_20220112_models.GetMachineGroupResponse: + """ + @summary get machine group + + @return: GetMachineGroupResponse + """ runtime = util_models.RuntimeOptions() headers = {} - return self.get_llmproject_with_options(project_id, headers, runtime) + return self.get_machine_group_with_options(machine_group_id, headers, runtime) - async def get_llmproject_async( + async def get_machine_group_async( self, - project_id: str, - ) -> pai_studio_20220112_models.GetLLMProjectResponse: + machine_group_id: str, + ) -> pai_studio_20220112_models.GetMachineGroupResponse: + """ + @summary get machine group + + @return: GetMachineGroupResponse + """ runtime = util_models.RuntimeOptions() headers = {} - return await self.get_llmproject_with_options_async(project_id, headers, runtime) + return await self.get_machine_group_with_options_async(machine_group_id, headers, runtime) - def get_llmservice_identity_role_with_options( + def get_metrics_with_options( self, - role_name: str, + request: pai_studio_20220112_models.GetMetricsRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.GetLLMServiceIdentityRoleResponse: + ) -> pai_studio_20220112_models.GetMetricsResponse: + """ + @summary 云监控 DescribeMetricList 代理 API + + @param request: GetMetricsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetMetricsResponse + """ + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.dimensions): + query['Dimensions'] = request.dimensions + if not UtilClient.is_unset(request.end_time): + query['EndTime'] = request.end_time + if not UtilClient.is_unset(request.express): + query['Express'] = request.express + if not UtilClient.is_unset(request.length): + query['Length'] = request.length + if not UtilClient.is_unset(request.metric_name): + query['MetricName'] = request.metric_name + if not UtilClient.is_unset(request.namespace): + query['Namespace'] = request.namespace + if not UtilClient.is_unset(request.next_token): + query['NextToken'] = request.next_token + if not UtilClient.is_unset(request.period): + query['Period'] = request.period + if not UtilClient.is_unset(request.start_time): + query['StartTime'] = request.start_time req = open_api_models.OpenApiRequest( - headers=headers + headers=headers, + query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='GetLLMServiceIdentityRole', + action='GetMetrics', version='2022-01-12', protocol='HTTPS', - pathname=f'/api/v1/langstudio/serviceidentityroles/{OpenApiUtilClient.get_encode_param(role_name)}', + pathname=f'/api/v1/quotas/cms/metrics', method='GET', auth_type='AK', style='ROA', @@ -3439,24 +3882,53 @@ def get_llmservice_identity_role_with_options( body_type='json' ) return TeaCore.from_map( - pai_studio_20220112_models.GetLLMServiceIdentityRoleResponse(), + pai_studio_20220112_models.GetMetricsResponse(), self.call_api(params, req, runtime) ) - async def get_llmservice_identity_role_with_options_async( + async def get_metrics_with_options_async( self, - role_name: str, + request: pai_studio_20220112_models.GetMetricsRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.GetLLMServiceIdentityRoleResponse: + ) -> pai_studio_20220112_models.GetMetricsResponse: + """ + @summary 云监控 DescribeMetricList 代理 API + + @param request: GetMetricsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetMetricsResponse + """ + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.dimensions): + query['Dimensions'] = request.dimensions + if not UtilClient.is_unset(request.end_time): + query['EndTime'] = request.end_time + if not UtilClient.is_unset(request.express): + query['Express'] = request.express + if not UtilClient.is_unset(request.length): + query['Length'] = request.length + if not UtilClient.is_unset(request.metric_name): + query['MetricName'] = request.metric_name + if not UtilClient.is_unset(request.namespace): + query['Namespace'] = request.namespace + if not UtilClient.is_unset(request.next_token): + query['NextToken'] = request.next_token + if not UtilClient.is_unset(request.period): + query['Period'] = request.period + if not UtilClient.is_unset(request.start_time): + query['StartTime'] = request.start_time req = open_api_models.OpenApiRequest( - headers=headers + headers=headers, + query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='GetLLMServiceIdentityRole', + action='GetMetrics', version='2022-01-12', protocol='HTTPS', - pathname=f'/api/v1/langstudio/serviceidentityroles/{OpenApiUtilClient.get_encode_param(role_name)}', + pathname=f'/api/v1/quotas/cms/metrics', method='GET', auth_type='AK', style='ROA', @@ -3464,41 +3936,72 @@ async def get_llmservice_identity_role_with_options_async( body_type='json' ) return TeaCore.from_map( - pai_studio_20220112_models.GetLLMServiceIdentityRoleResponse(), + pai_studio_20220112_models.GetMetricsResponse(), await self.call_api_async(params, req, runtime) ) - def get_llmservice_identity_role( + def get_metrics( self, - role_name: str, - ) -> pai_studio_20220112_models.GetLLMServiceIdentityRoleResponse: + request: pai_studio_20220112_models.GetMetricsRequest, + ) -> pai_studio_20220112_models.GetMetricsResponse: + """ + @summary 云监控 DescribeMetricList 代理 API + + @param request: GetMetricsRequest + @return: GetMetricsResponse + """ runtime = util_models.RuntimeOptions() headers = {} - return self.get_llmservice_identity_role_with_options(role_name, headers, runtime) + return self.get_metrics_with_options(request, headers, runtime) - async def get_llmservice_identity_role_async( + async def get_metrics_async( self, - role_name: str, - ) -> pai_studio_20220112_models.GetLLMServiceIdentityRoleResponse: + request: pai_studio_20220112_models.GetMetricsRequest, + ) -> pai_studio_20220112_models.GetMetricsResponse: + """ + @summary 云监控 DescribeMetricList 代理 API + + @param request: GetMetricsRequest + @return: GetMetricsResponse + """ runtime = util_models.RuntimeOptions() headers = {} - return await self.get_llmservice_identity_role_with_options_async(role_name, headers, runtime) + return await self.get_metrics_with_options_async(request, headers, runtime) - def get_llmsnapshot_with_options( + def get_node_gpumetrics_with_options( self, - project_id: str, - snapshot_id: str, + node_id: str, + request: pai_studio_20220112_models.GetNodeGPUMetricsRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.GetLLMSnapshotResponse: + ) -> pai_studio_20220112_models.GetNodeGPUMetricsResponse: + """ + @summary 查询节点的GPU指标 + + @param request: GetNodeGPUMetricsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetNodeGPUMetricsResponse + """ + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.end_time): + query['EndTime'] = request.end_time + if not UtilClient.is_unset(request.metric_type): + query['MetricType'] = request.metric_type + if not UtilClient.is_unset(request.quota_id): + query['QuotaId'] = request.quota_id + if not UtilClient.is_unset(request.start_time): + query['StartTime'] = request.start_time req = open_api_models.OpenApiRequest( - headers=headers + headers=headers, + query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='GetLLMSnapshot', + action='GetNodeGPUMetrics', version='2022-01-12', protocol='HTTPS', - pathname=f'/api/v1/langstudio/projects/{OpenApiUtilClient.get_encode_param(project_id)}/snapshots/{OpenApiUtilClient.get_encode_param(snapshot_id)}', + pathname=f'/api/v1/nodes/{OpenApiUtilClient.get_encode_param(node_id)}/gpumetrics', method='GET', auth_type='AK', style='ROA', @@ -3506,25 +4009,44 @@ def get_llmsnapshot_with_options( body_type='json' ) return TeaCore.from_map( - pai_studio_20220112_models.GetLLMSnapshotResponse(), + pai_studio_20220112_models.GetNodeGPUMetricsResponse(), self.call_api(params, req, runtime) ) - async def get_llmsnapshot_with_options_async( + async def get_node_gpumetrics_with_options_async( self, - project_id: str, - snapshot_id: str, + node_id: str, + request: pai_studio_20220112_models.GetNodeGPUMetricsRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.GetLLMSnapshotResponse: + ) -> pai_studio_20220112_models.GetNodeGPUMetricsResponse: + """ + @summary 查询节点的GPU指标 + + @param request: GetNodeGPUMetricsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetNodeGPUMetricsResponse + """ + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.end_time): + query['EndTime'] = request.end_time + if not UtilClient.is_unset(request.metric_type): + query['MetricType'] = request.metric_type + if not UtilClient.is_unset(request.quota_id): + query['QuotaId'] = request.quota_id + if not UtilClient.is_unset(request.start_time): + query['StartTime'] = request.start_time req = open_api_models.OpenApiRequest( - headers=headers + headers=headers, + query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='GetLLMSnapshot', + action='GetNodeGPUMetrics', version='2022-01-12', protocol='HTTPS', - pathname=f'/api/v1/langstudio/projects/{OpenApiUtilClient.get_encode_param(project_id)}/snapshots/{OpenApiUtilClient.get_encode_param(snapshot_id)}', + pathname=f'/api/v1/nodes/{OpenApiUtilClient.get_encode_param(node_id)}/gpumetrics', method='GET', auth_type='AK', style='ROA', @@ -3532,42 +4054,77 @@ async def get_llmsnapshot_with_options_async( body_type='json' ) return TeaCore.from_map( - pai_studio_20220112_models.GetLLMSnapshotResponse(), + pai_studio_20220112_models.GetNodeGPUMetricsResponse(), await self.call_api_async(params, req, runtime) ) - def get_llmsnapshot( + def get_node_gpumetrics( self, - project_id: str, - snapshot_id: str, - ) -> pai_studio_20220112_models.GetLLMSnapshotResponse: + node_id: str, + request: pai_studio_20220112_models.GetNodeGPUMetricsRequest, + ) -> pai_studio_20220112_models.GetNodeGPUMetricsResponse: + """ + @summary 查询节点的GPU指标 + + @param request: GetNodeGPUMetricsRequest + @return: GetNodeGPUMetricsResponse + """ runtime = util_models.RuntimeOptions() headers = {} - return self.get_llmsnapshot_with_options(project_id, snapshot_id, headers, runtime) + return self.get_node_gpumetrics_with_options(node_id, request, headers, runtime) - async def get_llmsnapshot_async( + async def get_node_gpumetrics_async( self, - project_id: str, - snapshot_id: str, - ) -> pai_studio_20220112_models.GetLLMSnapshotResponse: + node_id: str, + request: pai_studio_20220112_models.GetNodeGPUMetricsRequest, + ) -> pai_studio_20220112_models.GetNodeGPUMetricsResponse: + """ + @summary 查询节点的GPU指标 + + @param request: GetNodeGPUMetricsRequest + @return: GetNodeGPUMetricsResponse + """ runtime = util_models.RuntimeOptions() headers = {} - return await self.get_llmsnapshot_with_options_async(project_id, snapshot_id, headers, runtime) + return await self.get_node_gpumetrics_with_options_async(node_id, request, headers, runtime) - def get_machine_group_with_options( + def get_node_metrics_with_options( self, - machine_group_id: str, + resource_group_id: str, + metric_type: str, + request: pai_studio_20220112_models.GetNodeMetricsRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.GetMachineGroupResponse: + ) -> pai_studio_20220112_models.GetNodeMetricsResponse: + """ + @summary get resource group node metrics + + @param request: GetNodeMetricsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetNodeMetricsResponse + """ + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.end_time): + query['EndTime'] = request.end_time + if not UtilClient.is_unset(request.gputype): + query['GPUType'] = request.gputype + if not UtilClient.is_unset(request.start_time): + query['StartTime'] = request.start_time + if not UtilClient.is_unset(request.time_step): + query['TimeStep'] = request.time_step + if not UtilClient.is_unset(request.verbose): + query['Verbose'] = request.verbose req = open_api_models.OpenApiRequest( - headers=headers + headers=headers, + query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='GetMachineGroup', + action='GetNodeMetrics', version='2022-01-12', protocol='HTTPS', - pathname=f'/api/v1/resources/machinegroups/{OpenApiUtilClient.get_encode_param(machine_group_id)}', + pathname=f'/api/v1/resources/{OpenApiUtilClient.get_encode_param(resource_group_id)}/nodemetrics/{OpenApiUtilClient.get_encode_param(metric_type)}', method='GET', auth_type='AK', style='ROA', @@ -3575,99 +4132,26 @@ def get_machine_group_with_options( body_type='json' ) return TeaCore.from_map( - pai_studio_20220112_models.GetMachineGroupResponse(), + pai_studio_20220112_models.GetNodeMetricsResponse(), self.call_api(params, req, runtime) ) - async def get_machine_group_with_options_async( + async def get_node_metrics_with_options_async( self, - machine_group_id: str, - headers: Dict[str, str], - runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.GetMachineGroupResponse: - req = open_api_models.OpenApiRequest( - headers=headers - ) - params = open_api_models.Params( - action='GetMachineGroup', - version='2022-01-12', - protocol='HTTPS', - pathname=f'/api/v1/resources/machinegroups/{OpenApiUtilClient.get_encode_param(machine_group_id)}', - method='GET', - auth_type='AK', - style='ROA', - req_body_type='json', - body_type='json' - ) - return TeaCore.from_map( - pai_studio_20220112_models.GetMachineGroupResponse(), - await self.call_api_async(params, req, runtime) - ) - - def get_machine_group( - self, - machine_group_id: str, - ) -> pai_studio_20220112_models.GetMachineGroupResponse: - runtime = util_models.RuntimeOptions() - headers = {} - return self.get_machine_group_with_options(machine_group_id, headers, runtime) - - async def get_machine_group_async( - self, - machine_group_id: str, - ) -> pai_studio_20220112_models.GetMachineGroupResponse: - runtime = util_models.RuntimeOptions() - headers = {} - return await self.get_machine_group_with_options_async(machine_group_id, headers, runtime) - - def get_node_metrics_with_options( - self, - resource_group_id: str, - metric_type: str, - request: pai_studio_20220112_models.GetNodeMetricsRequest, - headers: Dict[str, str], - runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.GetNodeMetricsResponse: - UtilClient.validate_model(request) - query = {} - if not UtilClient.is_unset(request.end_time): - query['EndTime'] = request.end_time - if not UtilClient.is_unset(request.gputype): - query['GPUType'] = request.gputype - if not UtilClient.is_unset(request.start_time): - query['StartTime'] = request.start_time - if not UtilClient.is_unset(request.time_step): - query['TimeStep'] = request.time_step - if not UtilClient.is_unset(request.verbose): - query['Verbose'] = request.verbose - req = open_api_models.OpenApiRequest( - headers=headers, - query=OpenApiUtilClient.query(query) - ) - params = open_api_models.Params( - action='GetNodeMetrics', - version='2022-01-12', - protocol='HTTPS', - pathname=f'/api/v1/resources/{OpenApiUtilClient.get_encode_param(resource_group_id)}/nodemetrics/{OpenApiUtilClient.get_encode_param(metric_type)}', - method='GET', - auth_type='AK', - style='ROA', - req_body_type='json', - body_type='json' - ) - return TeaCore.from_map( - pai_studio_20220112_models.GetNodeMetricsResponse(), - self.call_api(params, req, runtime) - ) - - async def get_node_metrics_with_options_async( - self, - resource_group_id: str, - metric_type: str, - request: pai_studio_20220112_models.GetNodeMetricsRequest, + resource_group_id: str, + metric_type: str, + request: pai_studio_20220112_models.GetNodeMetricsRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetNodeMetricsResponse: + """ + @summary get resource group node metrics + + @param request: GetNodeMetricsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetNodeMetricsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.end_time): @@ -3706,6 +4190,12 @@ def get_node_metrics( metric_type: str, request: pai_studio_20220112_models.GetNodeMetricsRequest, ) -> pai_studio_20220112_models.GetNodeMetricsResponse: + """ + @summary get resource group node metrics + + @param request: GetNodeMetricsRequest + @return: GetNodeMetricsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.get_node_metrics_with_options(resource_group_id, metric_type, request, headers, runtime) @@ -3716,6 +4206,12 @@ async def get_node_metrics_async( metric_type: str, request: pai_studio_20220112_models.GetNodeMetricsRequest, ) -> pai_studio_20220112_models.GetNodeMetricsResponse: + """ + @summary get resource group node metrics + + @param request: GetNodeMetricsRequest + @return: GetNodeMetricsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.get_node_metrics_with_options_async(resource_group_id, metric_type, request, headers, runtime) @@ -3727,6 +4223,14 @@ def get_node_view_metrics_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetNodeViewMetricsResponse: + """ + @summary 获取节点视角的metrics + + @param request: GetNodeViewMetricsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetNodeViewMetricsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.node_id): @@ -3766,6 +4270,14 @@ async def get_node_view_metrics_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetNodeViewMetricsResponse: + """ + @summary 获取节点视角的metrics + + @param request: GetNodeViewMetricsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetNodeViewMetricsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.node_id): @@ -3803,6 +4315,12 @@ def get_node_view_metrics( resource_group_id: str, request: pai_studio_20220112_models.GetNodeViewMetricsRequest, ) -> pai_studio_20220112_models.GetNodeViewMetricsResponse: + """ + @summary 获取节点视角的metrics + + @param request: GetNodeViewMetricsRequest + @return: GetNodeViewMetricsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.get_node_view_metrics_with_options(resource_group_id, request, headers, runtime) @@ -3812,6 +4330,12 @@ async def get_node_view_metrics_async( resource_group_id: str, request: pai_studio_20220112_models.GetNodeViewMetricsRequest, ) -> pai_studio_20220112_models.GetNodeViewMetricsResponse: + """ + @summary 获取节点视角的metrics + + @param request: GetNodeViewMetricsRequest + @return: GetNodeViewMetricsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.get_node_view_metrics_with_options_async(resource_group_id, request, headers, runtime) @@ -3822,6 +4346,13 @@ def get_operation_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetOperationResponse: + """ + @summary 获取资源变更详情 + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetOperationResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -3847,6 +4378,13 @@ async def get_operation_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetOperationResponse: + """ + @summary 获取资源变更详情 + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetOperationResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -3870,6 +4408,11 @@ def get_operation( self, operation_id: str, ) -> pai_studio_20220112_models.GetOperationResponse: + """ + @summary 获取资源变更详情 + + @return: GetOperationResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.get_operation_with_options(operation_id, headers, runtime) @@ -3878,6 +4421,11 @@ async def get_operation_async( self, operation_id: str, ) -> pai_studio_20220112_models.GetOperationResponse: + """ + @summary 获取资源变更详情 + + @return: GetOperationResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.get_operation_with_options_async(operation_id, headers, runtime) @@ -3888,6 +4436,14 @@ def get_queue_infos_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetQueueInfosResponse: + """ + @summary 您可以通过GetQueueInfos得到一组队列的排队信息。 + + @param request: GetQueueInfosRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetQueueInfosResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.order): @@ -3932,6 +4488,14 @@ async def get_queue_infos_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetQueueInfosResponse: + """ + @summary 您可以通过GetQueueInfos得到一组队列的排队信息。 + + @param request: GetQueueInfosRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetQueueInfosResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.order): @@ -3974,6 +4538,12 @@ def get_queue_infos( self, request: pai_studio_20220112_models.GetQueueInfosRequest, ) -> pai_studio_20220112_models.GetQueueInfosResponse: + """ + @summary 您可以通过GetQueueInfos得到一组队列的排队信息。 + + @param request: GetQueueInfosRequest + @return: GetQueueInfosResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.get_queue_infos_with_options(request, headers, runtime) @@ -3982,6 +4552,12 @@ async def get_queue_infos_async( self, request: pai_studio_20220112_models.GetQueueInfosRequest, ) -> pai_studio_20220112_models.GetQueueInfosResponse: + """ + @summary 您可以通过GetQueueInfos得到一组队列的排队信息。 + + @param request: GetQueueInfosRequest + @return: GetQueueInfosResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.get_queue_infos_with_options_async(request, headers, runtime) @@ -3989,11 +4565,25 @@ async def get_queue_infos_async( def get_quota_with_options( self, quota_id: str, + request: pai_studio_20220112_models.GetQuotaRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetQuotaResponse: + """ + @summary 获取Quota + + @param request: GetQuotaRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetQuotaResponse + """ + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.verbose): + query['Verbose'] = request.verbose req = open_api_models.OpenApiRequest( - headers=headers + headers=headers, + query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( action='GetQuota', @@ -4014,11 +4604,25 @@ def get_quota_with_options( async def get_quota_with_options_async( self, quota_id: str, + request: pai_studio_20220112_models.GetQuotaRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetQuotaResponse: + """ + @summary 获取Quota + + @param request: GetQuotaRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetQuotaResponse + """ + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.verbose): + query['Verbose'] = request.verbose req = open_api_models.OpenApiRequest( - headers=headers + headers=headers, + query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( action='GetQuota', @@ -4039,18 +4643,32 @@ async def get_quota_with_options_async( def get_quota( self, quota_id: str, + request: pai_studio_20220112_models.GetQuotaRequest, ) -> pai_studio_20220112_models.GetQuotaResponse: + """ + @summary 获取Quota + + @param request: GetQuotaRequest + @return: GetQuotaResponse + """ runtime = util_models.RuntimeOptions() headers = {} - return self.get_quota_with_options(quota_id, headers, runtime) + return self.get_quota_with_options(quota_id, request, headers, runtime) async def get_quota_async( self, quota_id: str, + request: pai_studio_20220112_models.GetQuotaRequest, ) -> pai_studio_20220112_models.GetQuotaResponse: + """ + @summary 获取Quota + + @param request: GetQuotaRequest + @return: GetQuotaResponse + """ runtime = util_models.RuntimeOptions() headers = {} - return await self.get_quota_with_options_async(quota_id, headers, runtime) + return await self.get_quota_with_options_async(quota_id, request, headers, runtime) def get_quota_job_view_metrics_with_options( self, @@ -4059,6 +4677,14 @@ def get_quota_job_view_metrics_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetQuotaJobViewMetricsResponse: + """ + @summary 获取资源配额内运行的DLC、DSW任务的性能指标 + + @param request: GetQuotaJobViewMetricsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetQuotaJobViewMetricsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.end_time): @@ -4104,6 +4730,14 @@ async def get_quota_job_view_metrics_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetQuotaJobViewMetricsResponse: + """ + @summary 获取资源配额内运行的DLC、DSW任务的性能指标 + + @param request: GetQuotaJobViewMetricsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetQuotaJobViewMetricsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.end_time): @@ -4147,6 +4781,12 @@ def get_quota_job_view_metrics( quota_id: str, request: pai_studio_20220112_models.GetQuotaJobViewMetricsRequest, ) -> pai_studio_20220112_models.GetQuotaJobViewMetricsResponse: + """ + @summary 获取资源配额内运行的DLC、DSW任务的性能指标 + + @param request: GetQuotaJobViewMetricsRequest + @return: GetQuotaJobViewMetricsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.get_quota_job_view_metrics_with_options(quota_id, request, headers, runtime) @@ -4156,6 +4796,12 @@ async def get_quota_job_view_metrics_async( quota_id: str, request: pai_studio_20220112_models.GetQuotaJobViewMetricsRequest, ) -> pai_studio_20220112_models.GetQuotaJobViewMetricsResponse: + """ + @summary 获取资源配额内运行的DLC、DSW任务的性能指标 + + @param request: GetQuotaJobViewMetricsRequest + @return: GetQuotaJobViewMetricsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.get_quota_job_view_metrics_with_options_async(quota_id, request, headers, runtime) @@ -4168,6 +4814,14 @@ def get_quota_metrics_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetQuotaMetricsResponse: + """ + @summary 资源配额组维度指标 + + @param request: GetQuotaMetricsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetQuotaMetricsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.end_time): @@ -4206,6 +4860,14 @@ async def get_quota_metrics_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetQuotaMetricsResponse: + """ + @summary 资源配额组维度指标 + + @param request: GetQuotaMetricsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetQuotaMetricsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.end_time): @@ -4242,6 +4904,12 @@ def get_quota_metrics( metric_type: str, request: pai_studio_20220112_models.GetQuotaMetricsRequest, ) -> pai_studio_20220112_models.GetQuotaMetricsResponse: + """ + @summary 资源配额组维度指标 + + @param request: GetQuotaMetricsRequest + @return: GetQuotaMetricsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.get_quota_metrics_with_options(quota_id, metric_type, request, headers, runtime) @@ -4252,6 +4920,12 @@ async def get_quota_metrics_async( metric_type: str, request: pai_studio_20220112_models.GetQuotaMetricsRequest, ) -> pai_studio_20220112_models.GetQuotaMetricsResponse: + """ + @summary 资源配额组维度指标 + + @param request: GetQuotaMetricsRequest + @return: GetQuotaMetricsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.get_quota_metrics_with_options_async(quota_id, metric_type, request, headers, runtime) @@ -4264,6 +4938,14 @@ def get_quota_node_metrics_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetQuotaNodeMetricsResponse: + """ + @summary 资源配额内节点指标 + + @param request: GetQuotaNodeMetricsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetQuotaNodeMetricsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.end_time): @@ -4304,6 +4986,14 @@ async def get_quota_node_metrics_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetQuotaNodeMetricsResponse: + """ + @summary 资源配额内节点指标 + + @param request: GetQuotaNodeMetricsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetQuotaNodeMetricsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.end_time): @@ -4342,6 +5032,12 @@ def get_quota_node_metrics( metric_type: str, request: pai_studio_20220112_models.GetQuotaNodeMetricsRequest, ) -> pai_studio_20220112_models.GetQuotaNodeMetricsResponse: + """ + @summary 资源配额内节点指标 + + @param request: GetQuotaNodeMetricsRequest + @return: GetQuotaNodeMetricsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.get_quota_node_metrics_with_options(quota_id, metric_type, request, headers, runtime) @@ -4352,6 +5048,12 @@ async def get_quota_node_metrics_async( metric_type: str, request: pai_studio_20220112_models.GetQuotaNodeMetricsRequest, ) -> pai_studio_20220112_models.GetQuotaNodeMetricsResponse: + """ + @summary 资源配额内节点指标 + + @param request: GetQuotaNodeMetricsRequest + @return: GetQuotaNodeMetricsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.get_quota_node_metrics_with_options_async(quota_id, metric_type, request, headers, runtime) @@ -4363,6 +5065,14 @@ def get_quota_node_view_metrics_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetQuotaNodeViewMetricsResponse: + """ + @summary 获取资源配额内节点实时的性能指标 + + @param request: GetQuotaNodeViewMetricsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetQuotaNodeViewMetricsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.node_id): @@ -4414,6 +5124,14 @@ async def get_quota_node_view_metrics_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetQuotaNodeViewMetricsResponse: + """ + @summary 获取资源配额内节点实时的性能指标 + + @param request: GetQuotaNodeViewMetricsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetQuotaNodeViewMetricsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.node_id): @@ -4463,6 +5181,12 @@ def get_quota_node_view_metrics( quota_id: str, request: pai_studio_20220112_models.GetQuotaNodeViewMetricsRequest, ) -> pai_studio_20220112_models.GetQuotaNodeViewMetricsResponse: + """ + @summary 获取资源配额内节点实时的性能指标 + + @param request: GetQuotaNodeViewMetricsRequest + @return: GetQuotaNodeViewMetricsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.get_quota_node_view_metrics_with_options(quota_id, request, headers, runtime) @@ -4472,6 +5196,12 @@ async def get_quota_node_view_metrics_async( quota_id: str, request: pai_studio_20220112_models.GetQuotaNodeViewMetricsRequest, ) -> pai_studio_20220112_models.GetQuotaNodeViewMetricsResponse: + """ + @summary 获取资源配额内节点实时的性能指标 + + @param request: GetQuotaNodeViewMetricsRequest + @return: GetQuotaNodeViewMetricsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.get_quota_node_view_metrics_with_options_async(quota_id, request, headers, runtime) @@ -4483,6 +5213,14 @@ def get_quota_queue_info_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetQuotaQueueInfoResponse: + """ + @summary 您可以通过 GetQuotaQueueInfo得到使用当前Quota的实例的排队信息。 + + @param request: GetQuotaQueueInfoRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetQuotaQueueInfoResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.before_workload_id): @@ -4534,6 +5272,14 @@ async def get_quota_queue_info_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetQuotaQueueInfoResponse: + """ + @summary 您可以通过 GetQuotaQueueInfo得到使用当前Quota的实例的排队信息。 + + @param request: GetQuotaQueueInfoRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetQuotaQueueInfoResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.before_workload_id): @@ -4583,6 +5329,12 @@ def get_quota_queue_info( quota_id: str, request: pai_studio_20220112_models.GetQuotaQueueInfoRequest, ) -> pai_studio_20220112_models.GetQuotaQueueInfoResponse: + """ + @summary 您可以通过 GetQuotaQueueInfo得到使用当前Quota的实例的排队信息。 + + @param request: GetQuotaQueueInfoRequest + @return: GetQuotaQueueInfoResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.get_quota_queue_info_with_options(quota_id, request, headers, runtime) @@ -4592,6 +5344,12 @@ async def get_quota_queue_info_async( quota_id: str, request: pai_studio_20220112_models.GetQuotaQueueInfoRequest, ) -> pai_studio_20220112_models.GetQuotaQueueInfoResponse: + """ + @summary 您可以通过 GetQuotaQueueInfo得到使用当前Quota的实例的排队信息。 + + @param request: GetQuotaQueueInfoRequest + @return: GetQuotaQueueInfoResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.get_quota_queue_info_with_options_async(quota_id, request, headers, runtime) @@ -4603,6 +5361,14 @@ def get_quota_range_user_view_metrics_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetQuotaRangeUserViewMetricsResponse: + """ + @summary 获取资源配额用户视图的历史资源使用情况 + + @param request: GetQuotaRangeUserViewMetricsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetQuotaRangeUserViewMetricsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.end_time): @@ -4648,6 +5414,14 @@ async def get_quota_range_user_view_metrics_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetQuotaRangeUserViewMetricsResponse: + """ + @summary 获取资源配额用户视图的历史资源使用情况 + + @param request: GetQuotaRangeUserViewMetricsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetQuotaRangeUserViewMetricsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.end_time): @@ -4691,6 +5465,12 @@ def get_quota_range_user_view_metrics( quota_id: str, request: pai_studio_20220112_models.GetQuotaRangeUserViewMetricsRequest, ) -> pai_studio_20220112_models.GetQuotaRangeUserViewMetricsResponse: + """ + @summary 获取资源配额用户视图的历史资源使用情况 + + @param request: GetQuotaRangeUserViewMetricsRequest + @return: GetQuotaRangeUserViewMetricsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.get_quota_range_user_view_metrics_with_options(quota_id, request, headers, runtime) @@ -4700,10 +5480,132 @@ async def get_quota_range_user_view_metrics_async( quota_id: str, request: pai_studio_20220112_models.GetQuotaRangeUserViewMetricsRequest, ) -> pai_studio_20220112_models.GetQuotaRangeUserViewMetricsResponse: + """ + @summary 获取资源配额用户视图的历史资源使用情况 + + @param request: GetQuotaRangeUserViewMetricsRequest + @return: GetQuotaRangeUserViewMetricsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.get_quota_range_user_view_metrics_with_options_async(quota_id, request, headers, runtime) + def get_quota_topo_with_options( + self, + quota_id: str, + request: pai_studio_20220112_models.GetQuotaTopoRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.GetQuotaTopoResponse: + """ + @summary 获取Quota拓扑信息 + + @param request: GetQuotaTopoRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetQuotaTopoResponse + """ + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.depth): + query['Depth'] = request.depth + if not UtilClient.is_unset(request.show_own_workloads): + query['ShowOwnWorkloads'] = request.show_own_workloads + if not UtilClient.is_unset(request.verbose): + query['Verbose'] = request.verbose + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='GetQuotaTopo', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/quotas/%5BQuotaId%5D/topo', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.GetQuotaTopoResponse(), + self.call_api(params, req, runtime) + ) + + async def get_quota_topo_with_options_async( + self, + quota_id: str, + request: pai_studio_20220112_models.GetQuotaTopoRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.GetQuotaTopoResponse: + """ + @summary 获取Quota拓扑信息 + + @param request: GetQuotaTopoRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetQuotaTopoResponse + """ + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.depth): + query['Depth'] = request.depth + if not UtilClient.is_unset(request.show_own_workloads): + query['ShowOwnWorkloads'] = request.show_own_workloads + if not UtilClient.is_unset(request.verbose): + query['Verbose'] = request.verbose + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='GetQuotaTopo', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/quotas/%5BQuotaId%5D/topo', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.GetQuotaTopoResponse(), + await self.call_api_async(params, req, runtime) + ) + + def get_quota_topo( + self, + quota_id: str, + request: pai_studio_20220112_models.GetQuotaTopoRequest, + ) -> pai_studio_20220112_models.GetQuotaTopoResponse: + """ + @summary 获取Quota拓扑信息 + + @param request: GetQuotaTopoRequest + @return: GetQuotaTopoResponse + """ + runtime = util_models.RuntimeOptions() + headers = {} + return self.get_quota_topo_with_options(quota_id, request, headers, runtime) + + async def get_quota_topo_async( + self, + quota_id: str, + request: pai_studio_20220112_models.GetQuotaTopoRequest, + ) -> pai_studio_20220112_models.GetQuotaTopoResponse: + """ + @summary 获取Quota拓扑信息 + + @param request: GetQuotaTopoRequest + @return: GetQuotaTopoResponse + """ + runtime = util_models.RuntimeOptions() + headers = {} + return await self.get_quota_topo_with_options_async(quota_id, request, headers, runtime) + def get_quota_user_view_metrics_with_options( self, quota_id: str, @@ -4711,6 +5613,14 @@ def get_quota_user_view_metrics_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetQuotaUserViewMetricsResponse: + """ + @summary 获取用户视图的资源使用情况 + + @param request: GetQuotaUserViewMetricsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetQuotaUserViewMetricsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.order): @@ -4754,6 +5664,14 @@ async def get_quota_user_view_metrics_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetQuotaUserViewMetricsResponse: + """ + @summary 获取用户视图的资源使用情况 + + @param request: GetQuotaUserViewMetricsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetQuotaUserViewMetricsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.order): @@ -4795,6 +5713,12 @@ def get_quota_user_view_metrics( quota_id: str, request: pai_studio_20220112_models.GetQuotaUserViewMetricsRequest, ) -> pai_studio_20220112_models.GetQuotaUserViewMetricsResponse: + """ + @summary 获取用户视图的资源使用情况 + + @param request: GetQuotaUserViewMetricsRequest + @return: GetQuotaUserViewMetricsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.get_quota_user_view_metrics_with_options(quota_id, request, headers, runtime) @@ -4804,6 +5728,12 @@ async def get_quota_user_view_metrics_async( quota_id: str, request: pai_studio_20220112_models.GetQuotaUserViewMetricsRequest, ) -> pai_studio_20220112_models.GetQuotaUserViewMetricsResponse: + """ + @summary 获取用户视图的资源使用情况 + + @param request: GetQuotaUserViewMetricsRequest + @return: GetQuotaUserViewMetricsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.get_quota_user_view_metrics_with_options_async(quota_id, request, headers, runtime) @@ -4815,6 +5745,14 @@ def get_range_user_view_metrics_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetRangeUserViewMetricsResponse: + """ + @summary 获取按照user统计的性能指标的历史数据 + + @param request: GetRangeUserViewMetricsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetRangeUserViewMetricsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.end_time): @@ -4860,6 +5798,14 @@ async def get_range_user_view_metrics_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetRangeUserViewMetricsResponse: + """ + @summary 获取按照user统计的性能指标的历史数据 + + @param request: GetRangeUserViewMetricsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetRangeUserViewMetricsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.end_time): @@ -4903,6 +5849,12 @@ def get_range_user_view_metrics( resource_group_id: str, request: pai_studio_20220112_models.GetRangeUserViewMetricsRequest, ) -> pai_studio_20220112_models.GetRangeUserViewMetricsResponse: + """ + @summary 获取按照user统计的性能指标的历史数据 + + @param request: GetRangeUserViewMetricsRequest + @return: GetRangeUserViewMetricsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.get_range_user_view_metrics_with_options(resource_group_id, request, headers, runtime) @@ -4912,6 +5864,12 @@ async def get_range_user_view_metrics_async( resource_group_id: str, request: pai_studio_20220112_models.GetRangeUserViewMetricsRequest, ) -> pai_studio_20220112_models.GetRangeUserViewMetricsResponse: + """ + @summary 获取按照user统计的性能指标的历史数据 + + @param request: GetRangeUserViewMetricsRequest + @return: GetRangeUserViewMetricsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.get_range_user_view_metrics_with_options_async(resource_group_id, request, headers, runtime) @@ -4923,6 +5881,14 @@ def get_resource_group_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetResourceGroupResponse: + """ + @summary get resource group by group id + + @param tmp_req: GetResourceGroupRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetResourceGroupResponse + """ UtilClient.validate_model(tmp_req) request = pai_studio_20220112_models.GetResourceGroupShrinkRequest() OpenApiUtilClient.convert(tmp_req, request) @@ -4960,6 +5926,14 @@ async def get_resource_group_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetResourceGroupResponse: + """ + @summary get resource group by group id + + @param tmp_req: GetResourceGroupRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetResourceGroupResponse + """ UtilClient.validate_model(tmp_req) request = pai_studio_20220112_models.GetResourceGroupShrinkRequest() OpenApiUtilClient.convert(tmp_req, request) @@ -4995,6 +5969,12 @@ def get_resource_group( resource_group_id: str, request: pai_studio_20220112_models.GetResourceGroupRequest, ) -> pai_studio_20220112_models.GetResourceGroupResponse: + """ + @summary get resource group by group id + + @param request: GetResourceGroupRequest + @return: GetResourceGroupResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.get_resource_group_with_options(resource_group_id, request, headers, runtime) @@ -5004,6 +5984,12 @@ async def get_resource_group_async( resource_group_id: str, request: pai_studio_20220112_models.GetResourceGroupRequest, ) -> pai_studio_20220112_models.GetResourceGroupResponse: + """ + @summary get resource group by group id + + @param request: GetResourceGroupRequest + @return: GetResourceGroupResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.get_resource_group_with_options_async(resource_group_id, request, headers, runtime) @@ -5016,6 +6002,14 @@ def get_resource_group_machine_group_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetResourceGroupMachineGroupResponse: + """ + @summary get machine group + + @param tmp_req: GetResourceGroupMachineGroupRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetResourceGroupMachineGroupResponse + """ UtilClient.validate_model(tmp_req) request = pai_studio_20220112_models.GetResourceGroupMachineGroupShrinkRequest() OpenApiUtilClient.convert(tmp_req, request) @@ -5052,6 +6046,14 @@ async def get_resource_group_machine_group_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetResourceGroupMachineGroupResponse: + """ + @summary get machine group + + @param tmp_req: GetResourceGroupMachineGroupRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetResourceGroupMachineGroupResponse + """ UtilClient.validate_model(tmp_req) request = pai_studio_20220112_models.GetResourceGroupMachineGroupShrinkRequest() OpenApiUtilClient.convert(tmp_req, request) @@ -5086,6 +6088,12 @@ def get_resource_group_machine_group( resource_group_id: str, request: pai_studio_20220112_models.GetResourceGroupMachineGroupRequest, ) -> pai_studio_20220112_models.GetResourceGroupMachineGroupResponse: + """ + @summary get machine group + + @param request: GetResourceGroupMachineGroupRequest + @return: GetResourceGroupMachineGroupResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.get_resource_group_machine_group_with_options(machine_group_id, resource_group_id, request, headers, runtime) @@ -5096,6 +6104,12 @@ async def get_resource_group_machine_group_async( resource_group_id: str, request: pai_studio_20220112_models.GetResourceGroupMachineGroupRequest, ) -> pai_studio_20220112_models.GetResourceGroupMachineGroupResponse: + """ + @summary get machine group + + @param request: GetResourceGroupMachineGroupRequest + @return: GetResourceGroupMachineGroupResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.get_resource_group_machine_group_with_options_async(machine_group_id, resource_group_id, request, headers, runtime) @@ -5108,6 +6122,14 @@ def get_resource_group_metrics_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetResourceGroupMetricsResponse: + """ + @summary 获取资源组卡型的使用率 + + @param request: GetResourceGroupMetricsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetResourceGroupMetricsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.end_time): @@ -5146,6 +6168,14 @@ async def get_resource_group_metrics_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetResourceGroupMetricsResponse: + """ + @summary 获取资源组卡型的使用率 + + @param request: GetResourceGroupMetricsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetResourceGroupMetricsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.end_time): @@ -5182,6 +6212,12 @@ def get_resource_group_metrics( metric_type: str, request: pai_studio_20220112_models.GetResourceGroupMetricsRequest, ) -> pai_studio_20220112_models.GetResourceGroupMetricsResponse: + """ + @summary 获取资源组卡型的使用率 + + @param request: GetResourceGroupMetricsRequest + @return: GetResourceGroupMetricsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.get_resource_group_metrics_with_options(resource_group_id, metric_type, request, headers, runtime) @@ -5192,6 +6228,12 @@ async def get_resource_group_metrics_async( metric_type: str, request: pai_studio_20220112_models.GetResourceGroupMetricsRequest, ) -> pai_studio_20220112_models.GetResourceGroupMetricsResponse: + """ + @summary 获取资源组卡型的使用率 + + @param request: GetResourceGroupMetricsRequest + @return: GetResourceGroupMetricsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.get_resource_group_metrics_with_options_async(resource_group_id, metric_type, request, headers, runtime) @@ -5202,6 +6244,14 @@ def get_resource_group_request_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetResourceGroupRequestResponse: + """ + @summary get resource group requested resource by resource group id + + @param request: GetResourceGroupRequestRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetResourceGroupRequestResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.pod_status): @@ -5234,6 +6284,14 @@ async def get_resource_group_request_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetResourceGroupRequestResponse: + """ + @summary get resource group requested resource by resource group id + + @param request: GetResourceGroupRequestRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetResourceGroupRequestResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.pod_status): @@ -5264,6 +6322,12 @@ def get_resource_group_request( self, request: pai_studio_20220112_models.GetResourceGroupRequestRequest, ) -> pai_studio_20220112_models.GetResourceGroupRequestResponse: + """ + @summary get resource group requested resource by resource group id + + @param request: GetResourceGroupRequestRequest + @return: GetResourceGroupRequestResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.get_resource_group_request_with_options(request, headers, runtime) @@ -5272,6 +6336,12 @@ async def get_resource_group_request_async( self, request: pai_studio_20220112_models.GetResourceGroupRequestRequest, ) -> pai_studio_20220112_models.GetResourceGroupRequestResponse: + """ + @summary get resource group requested resource by resource group id + + @param request: GetResourceGroupRequestRequest + @return: GetResourceGroupRequestResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.get_resource_group_request_with_options_async(request, headers, runtime) @@ -5282,6 +6352,14 @@ def get_resource_group_total_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetResourceGroupTotalResponse: + """ + @summary get resource group total resource by group id + + @param request: GetResourceGroupTotalRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetResourceGroupTotalResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.resource_group_id): @@ -5312,6 +6390,14 @@ async def get_resource_group_total_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetResourceGroupTotalResponse: + """ + @summary get resource group total resource by group id + + @param request: GetResourceGroupTotalRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetResourceGroupTotalResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.resource_group_id): @@ -5340,6 +6426,12 @@ def get_resource_group_total( self, request: pai_studio_20220112_models.GetResourceGroupTotalRequest, ) -> pai_studio_20220112_models.GetResourceGroupTotalResponse: + """ + @summary get resource group total resource by group id + + @param request: GetResourceGroupTotalRequest + @return: GetResourceGroupTotalResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.get_resource_group_total_with_options(request, headers, runtime) @@ -5348,6 +6440,12 @@ async def get_resource_group_total_async( self, request: pai_studio_20220112_models.GetResourceGroupTotalRequest, ) -> pai_studio_20220112_models.GetResourceGroupTotalResponse: + """ + @summary get resource group total resource by group id + + @param request: GetResourceGroupTotalRequest + @return: GetResourceGroupTotalResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.get_resource_group_total_with_options_async(request, headers, runtime) @@ -5358,6 +6456,13 @@ def get_service_identity_role_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetServiceIdentityRoleResponse: + """ + @summary 获取服务认证角色 + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetServiceIdentityRoleResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -5383,6 +6488,13 @@ async def get_service_identity_role_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetServiceIdentityRoleResponse: + """ + @summary 获取服务认证角色 + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetServiceIdentityRoleResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -5406,6 +6518,11 @@ def get_service_identity_role( self, role_name: str, ) -> pai_studio_20220112_models.GetServiceIdentityRoleResponse: + """ + @summary 获取服务认证角色 + + @return: GetServiceIdentityRoleResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.get_service_identity_role_with_options(role_name, headers, runtime) @@ -5414,6 +6531,11 @@ async def get_service_identity_role_async( self, role_name: str, ) -> pai_studio_20220112_models.GetServiceIdentityRoleResponse: + """ + @summary 获取服务认证角色 + + @return: GetServiceIdentityRoleResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.get_service_identity_role_with_options_async(role_name, headers, runtime) @@ -5425,6 +6547,14 @@ def get_spot_price_history_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetSpotPriceHistoryResponse: + """ + @summary 获取抢占式实例历史价格 + + @param request: GetSpotPriceHistoryRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetSpotPriceHistoryResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.end_time): @@ -5466,6 +6596,14 @@ async def get_spot_price_history_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetSpotPriceHistoryResponse: + """ + @summary 获取抢占式实例历史价格 + + @param request: GetSpotPriceHistoryRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetSpotPriceHistoryResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.end_time): @@ -5505,6 +6643,12 @@ def get_spot_price_history( instance_type: str, request: pai_studio_20220112_models.GetSpotPriceHistoryRequest, ) -> pai_studio_20220112_models.GetSpotPriceHistoryResponse: + """ + @summary 获取抢占式实例历史价格 + + @param request: GetSpotPriceHistoryRequest + @return: GetSpotPriceHistoryResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.get_spot_price_history_with_options(instance_type, request, headers, runtime) @@ -5514,6 +6658,12 @@ async def get_spot_price_history_async( instance_type: str, request: pai_studio_20220112_models.GetSpotPriceHistoryRequest, ) -> pai_studio_20220112_models.GetSpotPriceHistoryResponse: + """ + @summary 获取抢占式实例历史价格 + + @param request: GetSpotPriceHistoryRequest + @return: GetSpotPriceHistoryResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.get_spot_price_history_with_options_async(instance_type, request, headers, runtime) @@ -5524,6 +6674,13 @@ def get_spot_stock_preview_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetSpotStockPreviewResponse: + """ + @summary 获取抢占式实例的库存概览 + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetSpotStockPreviewResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -5549,6 +6706,13 @@ async def get_spot_stock_preview_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetSpotStockPreviewResponse: + """ + @summary 获取抢占式实例的库存概览 + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetSpotStockPreviewResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -5572,6 +6736,11 @@ def get_spot_stock_preview( self, instance_type: str, ) -> pai_studio_20220112_models.GetSpotStockPreviewResponse: + """ + @summary 获取抢占式实例的库存概览 + + @return: GetSpotStockPreviewResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.get_spot_stock_preview_with_options(instance_type, headers, runtime) @@ -5580,6 +6749,11 @@ async def get_spot_stock_preview_async( self, instance_type: str, ) -> pai_studio_20220112_models.GetSpotStockPreviewResponse: + """ + @summary 获取抢占式实例的库存概览 + + @return: GetSpotStockPreviewResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.get_spot_stock_preview_with_options_async(instance_type, headers, runtime) @@ -5590,6 +6764,14 @@ def get_token_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetTokenResponse: + """ + @summary 调用GetToken获取临时鉴权信息 + + @param request: GetTokenRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetTokenResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.expire_time): @@ -5622,6 +6804,14 @@ async def get_token_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetTokenResponse: + """ + @summary 调用GetToken获取临时鉴权信息 + + @param request: GetTokenRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetTokenResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.expire_time): @@ -5652,6 +6842,12 @@ def get_token( self, request: pai_studio_20220112_models.GetTokenRequest, ) -> pai_studio_20220112_models.GetTokenResponse: + """ + @summary 调用GetToken获取临时鉴权信息 + + @param request: GetTokenRequest + @return: GetTokenResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.get_token_with_options(request, headers, runtime) @@ -5660,6 +6856,12 @@ async def get_token_async( self, request: pai_studio_20220112_models.GetTokenRequest, ) -> pai_studio_20220112_models.GetTokenResponse: + """ + @summary 调用GetToken获取临时鉴权信息 + + @param request: GetTokenRequest + @return: GetTokenResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.get_token_with_options_async(request, headers, runtime) @@ -5671,6 +6873,14 @@ def get_training_job_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetTrainingJobResponse: + """ + @summary 获取TrainingJob的详情 + + @param request: GetTrainingJobRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetTrainingJobResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.token): @@ -5702,6 +6912,14 @@ async def get_training_job_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetTrainingJobResponse: + """ + @summary 获取TrainingJob的详情 + + @param request: GetTrainingJobRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetTrainingJobResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.token): @@ -5731,6 +6949,12 @@ def get_training_job( training_job_id: str, request: pai_studio_20220112_models.GetTrainingJobRequest, ) -> pai_studio_20220112_models.GetTrainingJobResponse: + """ + @summary 获取TrainingJob的详情 + + @param request: GetTrainingJobRequest + @return: GetTrainingJobResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.get_training_job_with_options(training_job_id, request, headers, runtime) @@ -5740,6 +6964,12 @@ async def get_training_job_async( training_job_id: str, request: pai_studio_20220112_models.GetTrainingJobRequest, ) -> pai_studio_20220112_models.GetTrainingJobResponse: + """ + @summary 获取TrainingJob的详情 + + @param request: GetTrainingJobRequest + @return: GetTrainingJobResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.get_training_job_with_options_async(training_job_id, request, headers, runtime) @@ -5751,6 +6981,14 @@ def get_training_job_error_info_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetTrainingJobErrorInfoResponse: + """ + @summary 获取Training Job的算法错误信息 + + @param request: GetTrainingJobErrorInfoRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetTrainingJobErrorInfoResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.token): @@ -5782,6 +7020,14 @@ async def get_training_job_error_info_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetTrainingJobErrorInfoResponse: + """ + @summary 获取Training Job的算法错误信息 + + @param request: GetTrainingJobErrorInfoRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetTrainingJobErrorInfoResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.token): @@ -5811,6 +7057,12 @@ def get_training_job_error_info( training_job_id: str, request: pai_studio_20220112_models.GetTrainingJobErrorInfoRequest, ) -> pai_studio_20220112_models.GetTrainingJobErrorInfoResponse: + """ + @summary 获取Training Job的算法错误信息 + + @param request: GetTrainingJobErrorInfoRequest + @return: GetTrainingJobErrorInfoResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.get_training_job_error_info_with_options(training_job_id, request, headers, runtime) @@ -5820,6 +7072,12 @@ async def get_training_job_error_info_async( training_job_id: str, request: pai_studio_20220112_models.GetTrainingJobErrorInfoRequest, ) -> pai_studio_20220112_models.GetTrainingJobErrorInfoResponse: + """ + @summary 获取Training Job的算法错误信息 + + @param request: GetTrainingJobErrorInfoRequest + @return: GetTrainingJobErrorInfoResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.get_training_job_error_info_with_options_async(training_job_id, request, headers, runtime) @@ -5831,6 +7089,14 @@ def get_training_job_latest_metrics_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetTrainingJobLatestMetricsResponse: + """ + @summary 获取TrainingJob最近的Metrics + + @param request: GetTrainingJobLatestMetricsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetTrainingJobLatestMetricsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.names): @@ -5864,6 +7130,14 @@ async def get_training_job_latest_metrics_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetTrainingJobLatestMetricsResponse: + """ + @summary 获取TrainingJob最近的Metrics + + @param request: GetTrainingJobLatestMetricsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetTrainingJobLatestMetricsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.names): @@ -5895,6 +7169,12 @@ def get_training_job_latest_metrics( training_job_id: str, request: pai_studio_20220112_models.GetTrainingJobLatestMetricsRequest, ) -> pai_studio_20220112_models.GetTrainingJobLatestMetricsResponse: + """ + @summary 获取TrainingJob最近的Metrics + + @param request: GetTrainingJobLatestMetricsRequest + @return: GetTrainingJobLatestMetricsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.get_training_job_latest_metrics_with_options(training_job_id, request, headers, runtime) @@ -5904,6 +7184,12 @@ async def get_training_job_latest_metrics_async( training_job_id: str, request: pai_studio_20220112_models.GetTrainingJobLatestMetricsRequest, ) -> pai_studio_20220112_models.GetTrainingJobLatestMetricsResponse: + """ + @summary 获取TrainingJob最近的Metrics + + @param request: GetTrainingJobLatestMetricsRequest + @return: GetTrainingJobLatestMetricsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.get_training_job_latest_metrics_with_options_async(training_job_id, request, headers, runtime) @@ -5915,6 +7201,14 @@ def get_user_view_metrics_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetUserViewMetricsResponse: + """ + @summary get user view metrics + + @param request: GetUserViewMetricsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetUserViewMetricsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.order): @@ -5958,6 +7252,14 @@ async def get_user_view_metrics_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.GetUserViewMetricsResponse: + """ + @summary get user view metrics + + @param request: GetUserViewMetricsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: GetUserViewMetricsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.order): @@ -5999,6 +7301,12 @@ def get_user_view_metrics( resource_group_id: str, request: pai_studio_20220112_models.GetUserViewMetricsRequest, ) -> pai_studio_20220112_models.GetUserViewMetricsResponse: + """ + @summary get user view metrics + + @param request: GetUserViewMetricsRequest + @return: GetUserViewMetricsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.get_user_view_metrics_with_options(resource_group_id, request, headers, runtime) @@ -6008,6 +7316,12 @@ async def get_user_view_metrics_async( resource_group_id: str, request: pai_studio_20220112_models.GetUserViewMetricsRequest, ) -> pai_studio_20220112_models.GetUserViewMetricsResponse: + """ + @summary get user view metrics + + @param request: GetUserViewMetricsRequest + @return: GetUserViewMetricsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.get_user_view_metrics_with_options_async(resource_group_id, request, headers, runtime) @@ -6018,6 +7332,14 @@ def list_ai4dserivces_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListAI4DSerivcesResponse: + """ + @summary 获取AI4D服务列表 + + @param request: ListAI4DSerivcesRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListAI4DSerivcesResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.service_type): @@ -6050,6 +7372,14 @@ async def list_ai4dserivces_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListAI4DSerivcesResponse: + """ + @summary 获取AI4D服务列表 + + @param request: ListAI4DSerivcesRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListAI4DSerivcesResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.service_type): @@ -6080,6 +7410,12 @@ def list_ai4dserivces( self, request: pai_studio_20220112_models.ListAI4DSerivcesRequest, ) -> pai_studio_20220112_models.ListAI4DSerivcesResponse: + """ + @summary 获取AI4D服务列表 + + @param request: ListAI4DSerivcesRequest + @return: ListAI4DSerivcesResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.list_ai4dserivces_with_options(request, headers, runtime) @@ -6088,6 +7424,12 @@ async def list_ai4dserivces_async( self, request: pai_studio_20220112_models.ListAI4DSerivcesRequest, ) -> pai_studio_20220112_models.ListAI4DSerivcesResponse: + """ + @summary 获取AI4D服务列表 + + @param request: ListAI4DSerivcesRequest + @return: ListAI4DSerivcesResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.list_ai4dserivces_with_options_async(request, headers, runtime) @@ -6098,6 +7440,14 @@ def list_ai4dservice_templates_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListAI4DServiceTemplatesResponse: + """ + @summary 获取AI4D服务模板 + + @param request: ListAI4DServiceTemplatesRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListAI4DServiceTemplatesResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.service_type): @@ -6130,6 +7480,14 @@ async def list_ai4dservice_templates_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListAI4DServiceTemplatesResponse: + """ + @summary 获取AI4D服务模板 + + @param request: ListAI4DServiceTemplatesRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListAI4DServiceTemplatesResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.service_type): @@ -6160,6 +7518,12 @@ def list_ai4dservice_templates( self, request: pai_studio_20220112_models.ListAI4DServiceTemplatesRequest, ) -> pai_studio_20220112_models.ListAI4DServiceTemplatesResponse: + """ + @summary 获取AI4D服务模板 + + @param request: ListAI4DServiceTemplatesRequest + @return: ListAI4DServiceTemplatesResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.list_ai4dservice_templates_with_options(request, headers, runtime) @@ -6168,6 +7532,12 @@ async def list_ai4dservice_templates_async( self, request: pai_studio_20220112_models.ListAI4DServiceTemplatesRequest, ) -> pai_studio_20220112_models.ListAI4DServiceTemplatesResponse: + """ + @summary 获取AI4D服务模板 + + @param request: ListAI4DServiceTemplatesRequest + @return: ListAI4DServiceTemplatesResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.list_ai4dservice_templates_with_options_async(request, headers, runtime) @@ -6179,6 +7549,14 @@ def list_algorithm_versions_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListAlgorithmVersionsResponse: + """ + @summary 获取算法的所有版本信息 + + @param request: ListAlgorithmVersionsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListAlgorithmVersionsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.page_number): @@ -6212,6 +7590,14 @@ async def list_algorithm_versions_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListAlgorithmVersionsResponse: + """ + @summary 获取算法的所有版本信息 + + @param request: ListAlgorithmVersionsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListAlgorithmVersionsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.page_number): @@ -6243,6 +7629,12 @@ def list_algorithm_versions( algorithm_id: str, request: pai_studio_20220112_models.ListAlgorithmVersionsRequest, ) -> pai_studio_20220112_models.ListAlgorithmVersionsResponse: + """ + @summary 获取算法的所有版本信息 + + @param request: ListAlgorithmVersionsRequest + @return: ListAlgorithmVersionsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.list_algorithm_versions_with_options(algorithm_id, request, headers, runtime) @@ -6252,6 +7644,12 @@ async def list_algorithm_versions_async( algorithm_id: str, request: pai_studio_20220112_models.ListAlgorithmVersionsRequest, ) -> pai_studio_20220112_models.ListAlgorithmVersionsResponse: + """ + @summary 获取算法的所有版本信息 + + @param request: ListAlgorithmVersionsRequest + @return: ListAlgorithmVersionsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.list_algorithm_versions_with_options_async(algorithm_id, request, headers, runtime) @@ -6262,6 +7660,14 @@ def list_algorithms_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListAlgorithmsResponse: + """ + @summary 获取算法列表 + + @param request: ListAlgorithmsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListAlgorithmsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.algorithm_id): @@ -6302,6 +7708,14 @@ async def list_algorithms_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListAlgorithmsResponse: + """ + @summary 获取算法列表 + + @param request: ListAlgorithmsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListAlgorithmsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.algorithm_id): @@ -6340,6 +7754,12 @@ def list_algorithms( self, request: pai_studio_20220112_models.ListAlgorithmsRequest, ) -> pai_studio_20220112_models.ListAlgorithmsResponse: + """ + @summary 获取算法列表 + + @param request: ListAlgorithmsRequest + @return: ListAlgorithmsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.list_algorithms_with_options(request, headers, runtime) @@ -6348,6 +7768,12 @@ async def list_algorithms_async( self, request: pai_studio_20220112_models.ListAlgorithmsRequest, ) -> pai_studio_20220112_models.ListAlgorithmsResponse: + """ + @summary 获取算法列表 + + @param request: ListAlgorithmsRequest + @return: ListAlgorithmsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.list_algorithms_with_options_async(request, headers, runtime) @@ -6358,6 +7784,14 @@ def list_component_version_snapshots_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListComponentVersionSnapshotsResponse: + """ + @summary 更新组件版本快照 + + @param request: ListComponentVersionSnapshotsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListComponentVersionSnapshotsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.component_id): @@ -6400,6 +7834,14 @@ async def list_component_version_snapshots_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListComponentVersionSnapshotsResponse: + """ + @summary 更新组件版本快照 + + @param request: ListComponentVersionSnapshotsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListComponentVersionSnapshotsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.component_id): @@ -6440,6 +7882,12 @@ def list_component_version_snapshots( self, request: pai_studio_20220112_models.ListComponentVersionSnapshotsRequest, ) -> pai_studio_20220112_models.ListComponentVersionSnapshotsResponse: + """ + @summary 更新组件版本快照 + + @param request: ListComponentVersionSnapshotsRequest + @return: ListComponentVersionSnapshotsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.list_component_version_snapshots_with_options(request, headers, runtime) @@ -6448,6 +7896,12 @@ async def list_component_version_snapshots_async( self, request: pai_studio_20220112_models.ListComponentVersionSnapshotsRequest, ) -> pai_studio_20220112_models.ListComponentVersionSnapshotsResponse: + """ + @summary 更新组件版本快照 + + @param request: ListComponentVersionSnapshotsRequest + @return: ListComponentVersionSnapshotsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.list_component_version_snapshots_with_options_async(request, headers, runtime) @@ -6459,6 +7913,14 @@ def list_component_versions_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListComponentVersionsResponse: + """ + @summary 获取组件版本列表 + + @param tmp_req: ListComponentVersionsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListComponentVersionsResponse + """ UtilClient.validate_model(tmp_req) request = pai_studio_20220112_models.ListComponentVersionsShrinkRequest() OpenApiUtilClient.convert(tmp_req, request) @@ -6504,6 +7966,14 @@ async def list_component_versions_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListComponentVersionsResponse: + """ + @summary 获取组件版本列表 + + @param tmp_req: ListComponentVersionsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListComponentVersionsResponse + """ UtilClient.validate_model(tmp_req) request = pai_studio_20220112_models.ListComponentVersionsShrinkRequest() OpenApiUtilClient.convert(tmp_req, request) @@ -6547,6 +8017,12 @@ def list_component_versions( component_id: str, request: pai_studio_20220112_models.ListComponentVersionsRequest, ) -> pai_studio_20220112_models.ListComponentVersionsResponse: + """ + @summary 获取组件版本列表 + + @param request: ListComponentVersionsRequest + @return: ListComponentVersionsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.list_component_versions_with_options(component_id, request, headers, runtime) @@ -6556,6 +8032,12 @@ async def list_component_versions_async( component_id: str, request: pai_studio_20220112_models.ListComponentVersionsRequest, ) -> pai_studio_20220112_models.ListComponentVersionsResponse: + """ + @summary 获取组件版本列表 + + @param request: ListComponentVersionsRequest + @return: ListComponentVersionsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.list_component_versions_with_options_async(component_id, request, headers, runtime) @@ -6566,6 +8048,14 @@ def list_components_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListComponentsResponse: + """ + @summary 获取组件列表 + + @param tmp_req: ListComponentsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListComponentsResponse + """ UtilClient.validate_model(tmp_req) request = pai_studio_20220112_models.ListComponentsShrinkRequest() OpenApiUtilClient.convert(tmp_req, request) @@ -6618,6 +8108,14 @@ async def list_components_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListComponentsResponse: + """ + @summary 获取组件列表 + + @param tmp_req: ListComponentsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListComponentsResponse + """ UtilClient.validate_model(tmp_req) request = pai_studio_20220112_models.ListComponentsShrinkRequest() OpenApiUtilClient.convert(tmp_req, request) @@ -6668,6 +8166,12 @@ def list_components( self, request: pai_studio_20220112_models.ListComponentsRequest, ) -> pai_studio_20220112_models.ListComponentsResponse: + """ + @summary 获取组件列表 + + @param request: ListComponentsRequest + @return: ListComponentsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.list_components_with_options(request, headers, runtime) @@ -6676,6 +8180,12 @@ async def list_components_async( self, request: pai_studio_20220112_models.ListComponentsRequest, ) -> pai_studio_20220112_models.ListComponentsResponse: + """ + @summary 获取组件列表 + + @param request: ListComponentsRequest + @return: ListComponentsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.list_components_with_options_async(request, headers, runtime) @@ -6686,6 +8196,14 @@ def list_instance_jobs_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListInstanceJobsResponse: + """ + @summary 获取实例任务列表 + + @param request: ListInstanceJobsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListInstanceJobsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.instance_job_type): @@ -6726,6 +8244,14 @@ async def list_instance_jobs_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListInstanceJobsResponse: + """ + @summary 获取实例任务列表 + + @param request: ListInstanceJobsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListInstanceJobsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.instance_job_type): @@ -6764,6 +8290,12 @@ def list_instance_jobs( self, request: pai_studio_20220112_models.ListInstanceJobsRequest, ) -> pai_studio_20220112_models.ListInstanceJobsResponse: + """ + @summary 获取实例任务列表 + + @param request: ListInstanceJobsRequest + @return: ListInstanceJobsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.list_instance_jobs_with_options(request, headers, runtime) @@ -6772,132 +8304,52 @@ async def list_instance_jobs_async( self, request: pai_studio_20220112_models.ListInstanceJobsRequest, ) -> pai_studio_20220112_models.ListInstanceJobsResponse: + """ + @summary 获取实例任务列表 + + @param request: ListInstanceJobsRequest + @return: ListInstanceJobsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.list_instance_jobs_with_options_async(request, headers, runtime) - def list_llmprojects_with_options( - self, - request: pai_studio_20220112_models.ListLLMProjectsRequest, - headers: Dict[str, str], - runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.ListLLMProjectsResponse: - UtilClient.validate_model(request) - query = {} - if not UtilClient.is_unset(request.order): - query['Order'] = request.order - if not UtilClient.is_unset(request.page_number): - query['PageNumber'] = request.page_number - if not UtilClient.is_unset(request.page_size): - query['PageSize'] = request.page_size - if not UtilClient.is_unset(request.project_name): - query['ProjectName'] = request.project_name - if not UtilClient.is_unset(request.sort_by): - query['SortBy'] = request.sort_by - if not UtilClient.is_unset(request.workspace_id): - query['WorkspaceId'] = request.workspace_id - req = open_api_models.OpenApiRequest( - headers=headers, - query=OpenApiUtilClient.query(query) - ) - params = open_api_models.Params( - action='ListLLMProjects', - version='2022-01-12', - protocol='HTTPS', - pathname=f'/api/v1/langstudio/projects', - method='GET', - auth_type='AK', - style='ROA', - req_body_type='json', - body_type='json' - ) - return TeaCore.from_map( - pai_studio_20220112_models.ListLLMProjectsResponse(), - self.call_api(params, req, runtime) - ) - - async def list_llmprojects_with_options_async( + def list_node_gpumetrics_with_options( self, - request: pai_studio_20220112_models.ListLLMProjectsRequest, - headers: Dict[str, str], - runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.ListLLMProjectsResponse: - UtilClient.validate_model(request) - query = {} - if not UtilClient.is_unset(request.order): - query['Order'] = request.order - if not UtilClient.is_unset(request.page_number): - query['PageNumber'] = request.page_number - if not UtilClient.is_unset(request.page_size): - query['PageSize'] = request.page_size - if not UtilClient.is_unset(request.project_name): - query['ProjectName'] = request.project_name - if not UtilClient.is_unset(request.sort_by): - query['SortBy'] = request.sort_by - if not UtilClient.is_unset(request.workspace_id): - query['WorkspaceId'] = request.workspace_id - req = open_api_models.OpenApiRequest( - headers=headers, - query=OpenApiUtilClient.query(query) - ) - params = open_api_models.Params( - action='ListLLMProjects', - version='2022-01-12', - protocol='HTTPS', - pathname=f'/api/v1/langstudio/projects', - method='GET', - auth_type='AK', - style='ROA', - req_body_type='json', - body_type='json' - ) - return TeaCore.from_map( - pai_studio_20220112_models.ListLLMProjectsResponse(), - await self.call_api_async(params, req, runtime) - ) - - def list_llmprojects( - self, - request: pai_studio_20220112_models.ListLLMProjectsRequest, - ) -> pai_studio_20220112_models.ListLLMProjectsResponse: - runtime = util_models.RuntimeOptions() - headers = {} - return self.list_llmprojects_with_options(request, headers, runtime) - - async def list_llmprojects_async( - self, - request: pai_studio_20220112_models.ListLLMProjectsRequest, - ) -> pai_studio_20220112_models.ListLLMProjectsResponse: - runtime = util_models.RuntimeOptions() - headers = {} - return await self.list_llmprojects_with_options_async(request, headers, runtime) - - def list_llmsnapshots_with_options( - self, - project_id: str, - request: pai_studio_20220112_models.ListLLMSnapshotsRequest, + quota_id: str, + request: pai_studio_20220112_models.ListNodeGPUMetricsRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.ListLLMSnapshotsResponse: + ) -> pai_studio_20220112_models.ListNodeGPUMetricsResponse: + """ + @summary 查询某资源配额下所有节点的性能指标列表 + + @param request: ListNodeGPUMetricsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListNodeGPUMetricsResponse + """ UtilClient.validate_model(request) query = {} - if not UtilClient.is_unset(request.order): - query['Order'] = request.order - if not UtilClient.is_unset(request.page_number): - query['PageNumber'] = request.page_number - if not UtilClient.is_unset(request.page_size): - query['PageSize'] = request.page_size - if not UtilClient.is_unset(request.sort_by): - query['SortBy'] = request.sort_by + if not UtilClient.is_unset(request.end_time): + query['EndTime'] = request.end_time + if not UtilClient.is_unset(request.gputype): + query['GPUType'] = request.gputype + if not UtilClient.is_unset(request.metric_type): + query['MetricType'] = request.metric_type + if not UtilClient.is_unset(request.node_type): + query['NodeType'] = request.node_type + if not UtilClient.is_unset(request.start_time): + query['StartTime'] = request.start_time req = open_api_models.OpenApiRequest( headers=headers, query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='ListLLMSnapshots', + action='ListNodeGPUMetrics', version='2022-01-12', protocol='HTTPS', - pathname=f'/api/v1/langstudio/projects/{OpenApiUtilClient.get_encode_param(project_id)}/snapshots', + pathname=f'/api/v1/quotas/{OpenApiUtilClient.get_encode_param(quota_id)}/nodegpumetrics', method='GET', auth_type='AK', style='ROA', @@ -6905,36 +8357,46 @@ def list_llmsnapshots_with_options( body_type='json' ) return TeaCore.from_map( - pai_studio_20220112_models.ListLLMSnapshotsResponse(), + pai_studio_20220112_models.ListNodeGPUMetricsResponse(), self.call_api(params, req, runtime) ) - async def list_llmsnapshots_with_options_async( + async def list_node_gpumetrics_with_options_async( self, - project_id: str, - request: pai_studio_20220112_models.ListLLMSnapshotsRequest, + quota_id: str, + request: pai_studio_20220112_models.ListNodeGPUMetricsRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.ListLLMSnapshotsResponse: + ) -> pai_studio_20220112_models.ListNodeGPUMetricsResponse: + """ + @summary 查询某资源配额下所有节点的性能指标列表 + + @param request: ListNodeGPUMetricsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListNodeGPUMetricsResponse + """ UtilClient.validate_model(request) query = {} - if not UtilClient.is_unset(request.order): - query['Order'] = request.order - if not UtilClient.is_unset(request.page_number): - query['PageNumber'] = request.page_number - if not UtilClient.is_unset(request.page_size): - query['PageSize'] = request.page_size - if not UtilClient.is_unset(request.sort_by): - query['SortBy'] = request.sort_by + if not UtilClient.is_unset(request.end_time): + query['EndTime'] = request.end_time + if not UtilClient.is_unset(request.gputype): + query['GPUType'] = request.gputype + if not UtilClient.is_unset(request.metric_type): + query['MetricType'] = request.metric_type + if not UtilClient.is_unset(request.node_type): + query['NodeType'] = request.node_type + if not UtilClient.is_unset(request.start_time): + query['StartTime'] = request.start_time req = open_api_models.OpenApiRequest( headers=headers, query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='ListLLMSnapshots', + action='ListNodeGPUMetrics', version='2022-01-12', protocol='HTTPS', - pathname=f'/api/v1/langstudio/projects/{OpenApiUtilClient.get_encode_param(project_id)}/snapshots', + pathname=f'/api/v1/quotas/{OpenApiUtilClient.get_encode_param(quota_id)}/nodegpumetrics', method='GET', auth_type='AK', style='ROA', @@ -6942,27 +8404,39 @@ async def list_llmsnapshots_with_options_async( body_type='json' ) return TeaCore.from_map( - pai_studio_20220112_models.ListLLMSnapshotsResponse(), + pai_studio_20220112_models.ListNodeGPUMetricsResponse(), await self.call_api_async(params, req, runtime) ) - def list_llmsnapshots( + def list_node_gpumetrics( self, - project_id: str, - request: pai_studio_20220112_models.ListLLMSnapshotsRequest, - ) -> pai_studio_20220112_models.ListLLMSnapshotsResponse: + quota_id: str, + request: pai_studio_20220112_models.ListNodeGPUMetricsRequest, + ) -> pai_studio_20220112_models.ListNodeGPUMetricsResponse: + """ + @summary 查询某资源配额下所有节点的性能指标列表 + + @param request: ListNodeGPUMetricsRequest + @return: ListNodeGPUMetricsResponse + """ runtime = util_models.RuntimeOptions() headers = {} - return self.list_llmsnapshots_with_options(project_id, request, headers, runtime) + return self.list_node_gpumetrics_with_options(quota_id, request, headers, runtime) - async def list_llmsnapshots_async( + async def list_node_gpumetrics_async( self, - project_id: str, - request: pai_studio_20220112_models.ListLLMSnapshotsRequest, - ) -> pai_studio_20220112_models.ListLLMSnapshotsResponse: + quota_id: str, + request: pai_studio_20220112_models.ListNodeGPUMetricsRequest, + ) -> pai_studio_20220112_models.ListNodeGPUMetricsResponse: + """ + @summary 查询某资源配额下所有节点的性能指标列表 + + @param request: ListNodeGPUMetricsRequest + @return: ListNodeGPUMetricsResponse + """ runtime = util_models.RuntimeOptions() headers = {} - return await self.list_llmsnapshots_with_options_async(project_id, request, headers, runtime) + return await self.list_node_gpumetrics_with_options_async(quota_id, request, headers, runtime) def list_node_pods_with_options( self, @@ -6971,6 +8445,14 @@ def list_node_pods_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListNodePodsResponse: + """ + @summary 您可以通过ListNodePods得到节点上的Pod信息 + + @param request: ListNodePodsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListNodePodsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.resource_group_id): @@ -7002,6 +8484,14 @@ async def list_node_pods_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListNodePodsResponse: + """ + @summary 您可以通过ListNodePods得到节点上的Pod信息 + + @param request: ListNodePodsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListNodePodsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.resource_group_id): @@ -7031,6 +8521,12 @@ def list_node_pods( node_id: str, request: pai_studio_20220112_models.ListNodePodsRequest, ) -> pai_studio_20220112_models.ListNodePodsResponse: + """ + @summary 您可以通过ListNodePods得到节点上的Pod信息 + + @param request: ListNodePodsRequest + @return: ListNodePodsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.list_node_pods_with_options(node_id, request, headers, runtime) @@ -7040,6 +8536,12 @@ async def list_node_pods_async( node_id: str, request: pai_studio_20220112_models.ListNodePodsRequest, ) -> pai_studio_20220112_models.ListNodePodsResponse: + """ + @summary 您可以通过ListNodePods得到节点上的Pod信息 + + @param request: ListNodePodsRequest + @return: ListNodePodsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.list_node_pods_with_options_async(node_id, request, headers, runtime) @@ -7050,6 +8552,14 @@ def list_node_types_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListNodeTypesResponse: + """ + @summary 获取节点规格列表 + + @param request: ListNodeTypesRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListNodeTypesResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.accelerator_type): @@ -7088,6 +8598,14 @@ async def list_node_types_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListNodeTypesResponse: + """ + @summary 获取节点规格列表 + + @param request: ListNodeTypesRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListNodeTypesResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.accelerator_type): @@ -7124,6 +8642,12 @@ def list_node_types( self, request: pai_studio_20220112_models.ListNodeTypesRequest, ) -> pai_studio_20220112_models.ListNodeTypesResponse: + """ + @summary 获取节点规格列表 + + @param request: ListNodeTypesRequest + @return: ListNodeTypesResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.list_node_types_with_options(request, headers, runtime) @@ -7132,6 +8656,12 @@ async def list_node_types_async( self, request: pai_studio_20220112_models.ListNodeTypesRequest, ) -> pai_studio_20220112_models.ListNodeTypesResponse: + """ + @summary 获取节点规格列表 + + @param request: ListNodeTypesRequest + @return: ListNodeTypesResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.list_node_types_with_options_async(request, headers, runtime) @@ -7142,12 +8672,26 @@ def list_nodes_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListNodesResponse: + """ + @summary 获取资源节点列表 + + @param request: ListNodesRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListNodesResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.accelerator_type): query['AcceleratorType'] = request.accelerator_type + if not UtilClient.is_unset(request.filter_by_quota_id): + query['FilterByQuotaId'] = request.filter_by_quota_id + if not UtilClient.is_unset(request.filter_by_resource_group_ids): + query['FilterByResourceGroupIds'] = request.filter_by_resource_group_ids if not UtilClient.is_unset(request.gputype): query['GPUType'] = request.gputype + if not UtilClient.is_unset(request.node_names): + query['NodeNames'] = request.node_names if not UtilClient.is_unset(request.node_statuses): query['NodeStatuses'] = request.node_statuses if not UtilClient.is_unset(request.node_types): @@ -7194,12 +8738,26 @@ async def list_nodes_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListNodesResponse: + """ + @summary 获取资源节点列表 + + @param request: ListNodesRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListNodesResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.accelerator_type): query['AcceleratorType'] = request.accelerator_type + if not UtilClient.is_unset(request.filter_by_quota_id): + query['FilterByQuotaId'] = request.filter_by_quota_id + if not UtilClient.is_unset(request.filter_by_resource_group_ids): + query['FilterByResourceGroupIds'] = request.filter_by_resource_group_ids if not UtilClient.is_unset(request.gputype): query['GPUType'] = request.gputype + if not UtilClient.is_unset(request.node_names): + query['NodeNames'] = request.node_names if not UtilClient.is_unset(request.node_statuses): query['NodeStatuses'] = request.node_statuses if not UtilClient.is_unset(request.node_types): @@ -7244,6 +8802,12 @@ def list_nodes( self, request: pai_studio_20220112_models.ListNodesRequest, ) -> pai_studio_20220112_models.ListNodesResponse: + """ + @summary 获取资源节点列表 + + @param request: ListNodesRequest + @return: ListNodesResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.list_nodes_with_options(request, headers, runtime) @@ -7252,45 +8816,276 @@ async def list_nodes_async( self, request: pai_studio_20220112_models.ListNodesRequest, ) -> pai_studio_20220112_models.ListNodesResponse: + """ + @summary 获取资源节点列表 + + @param request: ListNodesRequest + @return: ListNodesResponse + """ + runtime = util_models.RuntimeOptions() + headers = {} + return await self.list_nodes_with_options_async(request, headers, runtime) + + def list_operations_with_options( + self, + request: pai_studio_20220112_models.ListOperationsRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.ListOperationsResponse: + """ + @summary 获取资源变更列表 + + @param request: ListOperationsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListOperationsResponse + """ + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.object_id): + query['ObjectId'] = request.object_id + if not UtilClient.is_unset(request.object_type): + query['ObjectType'] = request.object_type + if not UtilClient.is_unset(request.operation_id): + query['OperationId'] = request.operation_id + if not UtilClient.is_unset(request.operation_type): + query['OperationType'] = request.operation_type + if not UtilClient.is_unset(request.order): + query['Order'] = request.order + if not UtilClient.is_unset(request.page_number): + query['PageNumber'] = request.page_number + if not UtilClient.is_unset(request.page_size): + query['PageSize'] = request.page_size + if not UtilClient.is_unset(request.sort_by): + query['SortBy'] = request.sort_by + if not UtilClient.is_unset(request.status): + query['Status'] = request.status + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='ListOperations', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/operations', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.ListOperationsResponse(), + self.call_api(params, req, runtime) + ) + + async def list_operations_with_options_async( + self, + request: pai_studio_20220112_models.ListOperationsRequest, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.ListOperationsResponse: + """ + @summary 获取资源变更列表 + + @param request: ListOperationsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListOperationsResponse + """ + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.object_id): + query['ObjectId'] = request.object_id + if not UtilClient.is_unset(request.object_type): + query['ObjectType'] = request.object_type + if not UtilClient.is_unset(request.operation_id): + query['OperationId'] = request.operation_id + if not UtilClient.is_unset(request.operation_type): + query['OperationType'] = request.operation_type + if not UtilClient.is_unset(request.order): + query['Order'] = request.order + if not UtilClient.is_unset(request.page_number): + query['PageNumber'] = request.page_number + if not UtilClient.is_unset(request.page_size): + query['PageSize'] = request.page_size + if not UtilClient.is_unset(request.sort_by): + query['SortBy'] = request.sort_by + if not UtilClient.is_unset(request.status): + query['Status'] = request.status + req = open_api_models.OpenApiRequest( + headers=headers, + query=OpenApiUtilClient.query(query) + ) + params = open_api_models.Params( + action='ListOperations', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/operations', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.ListOperationsResponse(), + await self.call_api_async(params, req, runtime) + ) + + def list_operations( + self, + request: pai_studio_20220112_models.ListOperationsRequest, + ) -> pai_studio_20220112_models.ListOperationsResponse: + """ + @summary 获取资源变更列表 + + @param request: ListOperationsRequest + @return: ListOperationsResponse + """ + runtime = util_models.RuntimeOptions() + headers = {} + return self.list_operations_with_options(request, headers, runtime) + + async def list_operations_async( + self, + request: pai_studio_20220112_models.ListOperationsRequest, + ) -> pai_studio_20220112_models.ListOperationsResponse: + """ + @summary 获取资源变更列表 + + @param request: ListOperationsRequest + @return: ListOperationsResponse + """ + runtime = util_models.RuntimeOptions() + headers = {} + return await self.list_operations_with_options_async(request, headers, runtime) + + def list_permissions_with_options( + self, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.ListPermissionsResponse: + """ + @summary ListPermissions + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListPermissionsResponse + """ + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='ListPermissions', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/permissions', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.ListPermissionsResponse(), + self.call_api(params, req, runtime) + ) + + async def list_permissions_with_options_async( + self, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.ListPermissionsResponse: + """ + @summary ListPermissions + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListPermissionsResponse + """ + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='ListPermissions', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/permissions', + method='GET', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.ListPermissionsResponse(), + await self.call_api_async(params, req, runtime) + ) + + def list_permissions(self) -> pai_studio_20220112_models.ListPermissionsResponse: + """ + @summary ListPermissions + + @return: ListPermissionsResponse + """ + runtime = util_models.RuntimeOptions() + headers = {} + return self.list_permissions_with_options(headers, runtime) + + async def list_permissions_async(self) -> pai_studio_20220112_models.ListPermissionsResponse: + """ + @summary ListPermissions + + @return: ListPermissionsResponse + """ runtime = util_models.RuntimeOptions() headers = {} - return await self.list_nodes_with_options_async(request, headers, runtime) + return await self.list_permissions_with_options_async(headers, runtime) - def list_operations_with_options( + def list_quota_users_with_options( self, - request: pai_studio_20220112_models.ListOperationsRequest, + quota_id: str, + request: pai_studio_20220112_models.ListQuotaUsersRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.ListOperationsResponse: + ) -> pai_studio_20220112_models.ListQuotaUsersResponse: + """ + @summary 获取当前资源配额用户列表和其所使用的资源 + + @param request: ListQuotaUsersRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListQuotaUsersResponse + """ UtilClient.validate_model(request) query = {} - if not UtilClient.is_unset(request.object_id): - query['ObjectId'] = request.object_id - if not UtilClient.is_unset(request.object_type): - query['ObjectType'] = request.object_type - if not UtilClient.is_unset(request.operation_id): - query['OperationId'] = request.operation_id - if not UtilClient.is_unset(request.operation_type): - query['OperationType'] = request.operation_type if not UtilClient.is_unset(request.order): query['Order'] = request.order if not UtilClient.is_unset(request.page_number): query['PageNumber'] = request.page_number if not UtilClient.is_unset(request.page_size): query['PageSize'] = request.page_size + if not UtilClient.is_unset(request.self_only): + query['SelfOnly'] = request.self_only if not UtilClient.is_unset(request.sort_by): query['SortBy'] = request.sort_by - if not UtilClient.is_unset(request.status): - query['Status'] = request.status + if not UtilClient.is_unset(request.user_id): + query['UserId'] = request.user_id + if not UtilClient.is_unset(request.username): + query['Username'] = request.username + if not UtilClient.is_unset(request.workspace_id): + query['WorkspaceId'] = request.workspace_id req = open_api_models.OpenApiRequest( headers=headers, query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='ListOperations', + action='ListQuotaUsers', version='2022-01-12', protocol='HTTPS', - pathname=f'/api/v1/operations', + pathname=f'/api/v1/quotas/{OpenApiUtilClient.get_encode_param(quota_id)}/users', method='GET', auth_type='AK', style='ROA', @@ -7298,45 +9093,52 @@ def list_operations_with_options( body_type='json' ) return TeaCore.from_map( - pai_studio_20220112_models.ListOperationsResponse(), + pai_studio_20220112_models.ListQuotaUsersResponse(), self.call_api(params, req, runtime) ) - async def list_operations_with_options_async( + async def list_quota_users_with_options_async( self, - request: pai_studio_20220112_models.ListOperationsRequest, + quota_id: str, + request: pai_studio_20220112_models.ListQuotaUsersRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.ListOperationsResponse: + ) -> pai_studio_20220112_models.ListQuotaUsersResponse: + """ + @summary 获取当前资源配额用户列表和其所使用的资源 + + @param request: ListQuotaUsersRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListQuotaUsersResponse + """ UtilClient.validate_model(request) query = {} - if not UtilClient.is_unset(request.object_id): - query['ObjectId'] = request.object_id - if not UtilClient.is_unset(request.object_type): - query['ObjectType'] = request.object_type - if not UtilClient.is_unset(request.operation_id): - query['OperationId'] = request.operation_id - if not UtilClient.is_unset(request.operation_type): - query['OperationType'] = request.operation_type if not UtilClient.is_unset(request.order): query['Order'] = request.order if not UtilClient.is_unset(request.page_number): query['PageNumber'] = request.page_number if not UtilClient.is_unset(request.page_size): query['PageSize'] = request.page_size + if not UtilClient.is_unset(request.self_only): + query['SelfOnly'] = request.self_only if not UtilClient.is_unset(request.sort_by): query['SortBy'] = request.sort_by - if not UtilClient.is_unset(request.status): - query['Status'] = request.status + if not UtilClient.is_unset(request.user_id): + query['UserId'] = request.user_id + if not UtilClient.is_unset(request.username): + query['Username'] = request.username + if not UtilClient.is_unset(request.workspace_id): + query['WorkspaceId'] = request.workspace_id req = open_api_models.OpenApiRequest( headers=headers, query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='ListOperations', + action='ListQuotaUsers', version='2022-01-12', protocol='HTTPS', - pathname=f'/api/v1/operations', + pathname=f'/api/v1/quotas/{OpenApiUtilClient.get_encode_param(quota_id)}/users', method='GET', auth_type='AK', style='ROA', @@ -7344,39 +9146,94 @@ async def list_operations_with_options_async( body_type='json' ) return TeaCore.from_map( - pai_studio_20220112_models.ListOperationsResponse(), + pai_studio_20220112_models.ListQuotaUsersResponse(), await self.call_api_async(params, req, runtime) ) - def list_operations( + def list_quota_users( self, - request: pai_studio_20220112_models.ListOperationsRequest, - ) -> pai_studio_20220112_models.ListOperationsResponse: + quota_id: str, + request: pai_studio_20220112_models.ListQuotaUsersRequest, + ) -> pai_studio_20220112_models.ListQuotaUsersResponse: + """ + @summary 获取当前资源配额用户列表和其所使用的资源 + + @param request: ListQuotaUsersRequest + @return: ListQuotaUsersResponse + """ runtime = util_models.RuntimeOptions() headers = {} - return self.list_operations_with_options(request, headers, runtime) + return self.list_quota_users_with_options(quota_id, request, headers, runtime) - async def list_operations_async( + async def list_quota_users_async( self, - request: pai_studio_20220112_models.ListOperationsRequest, - ) -> pai_studio_20220112_models.ListOperationsResponse: + quota_id: str, + request: pai_studio_20220112_models.ListQuotaUsersRequest, + ) -> pai_studio_20220112_models.ListQuotaUsersResponse: + """ + @summary 获取当前资源配额用户列表和其所使用的资源 + + @param request: ListQuotaUsersRequest + @return: ListQuotaUsersResponse + """ runtime = util_models.RuntimeOptions() headers = {} - return await self.list_operations_with_options_async(request, headers, runtime) + return await self.list_quota_users_with_options_async(quota_id, request, headers, runtime) - def list_permissions_with_options( + def list_quota_workloads_with_options( self, + quota_id: str, + request: pai_studio_20220112_models.ListQuotaWorkloadsRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.ListPermissionsResponse: + ) -> pai_studio_20220112_models.ListQuotaWorkloadsResponse: + """ + @summary 您可以通过此API获取Quota上的任务信息 + + @param request: ListQuotaWorkloadsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListQuotaWorkloadsResponse + """ + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.before_workload_id): + query['BeforeWorkloadId'] = request.before_workload_id + if not UtilClient.is_unset(request.node_name): + query['NodeName'] = request.node_name + if not UtilClient.is_unset(request.order): + query['Order'] = request.order + if not UtilClient.is_unset(request.page_number): + query['PageNumber'] = request.page_number + if not UtilClient.is_unset(request.page_size): + query['PageSize'] = request.page_size + if not UtilClient.is_unset(request.show_own): + query['ShowOwn'] = request.show_own + if not UtilClient.is_unset(request.sort_by): + query['SortBy'] = request.sort_by + if not UtilClient.is_unset(request.status): + query['Status'] = request.status + if not UtilClient.is_unset(request.sub_quota_ids): + query['SubQuotaIds'] = request.sub_quota_ids + if not UtilClient.is_unset(request.user_ids): + query['UserIds'] = request.user_ids + if not UtilClient.is_unset(request.workload_created_time_range): + query['WorkloadCreatedTimeRange'] = request.workload_created_time_range + if not UtilClient.is_unset(request.workload_ids): + query['WorkloadIds'] = request.workload_ids + if not UtilClient.is_unset(request.workload_type): + query['WorkloadType'] = request.workload_type + if not UtilClient.is_unset(request.workspace_ids): + query['WorkspaceIds'] = request.workspace_ids req = open_api_models.OpenApiRequest( - headers=headers + headers=headers, + query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='ListPermissions', + action='ListQuotaWorkloads', version='2022-01-12', protocol='HTTPS', - pathname=f'/api/v1/permissions', + pathname=f'/api/v1/quotas/{OpenApiUtilClient.get_encode_param(quota_id)}/workloads', method='GET', auth_type='AK', style='ROA', @@ -7384,23 +9241,64 @@ def list_permissions_with_options( body_type='json' ) return TeaCore.from_map( - pai_studio_20220112_models.ListPermissionsResponse(), + pai_studio_20220112_models.ListQuotaWorkloadsResponse(), self.call_api(params, req, runtime) ) - async def list_permissions_with_options_async( + async def list_quota_workloads_with_options_async( self, + quota_id: str, + request: pai_studio_20220112_models.ListQuotaWorkloadsRequest, headers: Dict[str, str], runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.ListPermissionsResponse: + ) -> pai_studio_20220112_models.ListQuotaWorkloadsResponse: + """ + @summary 您可以通过此API获取Quota上的任务信息 + + @param request: ListQuotaWorkloadsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListQuotaWorkloadsResponse + """ + UtilClient.validate_model(request) + query = {} + if not UtilClient.is_unset(request.before_workload_id): + query['BeforeWorkloadId'] = request.before_workload_id + if not UtilClient.is_unset(request.node_name): + query['NodeName'] = request.node_name + if not UtilClient.is_unset(request.order): + query['Order'] = request.order + if not UtilClient.is_unset(request.page_number): + query['PageNumber'] = request.page_number + if not UtilClient.is_unset(request.page_size): + query['PageSize'] = request.page_size + if not UtilClient.is_unset(request.show_own): + query['ShowOwn'] = request.show_own + if not UtilClient.is_unset(request.sort_by): + query['SortBy'] = request.sort_by + if not UtilClient.is_unset(request.status): + query['Status'] = request.status + if not UtilClient.is_unset(request.sub_quota_ids): + query['SubQuotaIds'] = request.sub_quota_ids + if not UtilClient.is_unset(request.user_ids): + query['UserIds'] = request.user_ids + if not UtilClient.is_unset(request.workload_created_time_range): + query['WorkloadCreatedTimeRange'] = request.workload_created_time_range + if not UtilClient.is_unset(request.workload_ids): + query['WorkloadIds'] = request.workload_ids + if not UtilClient.is_unset(request.workload_type): + query['WorkloadType'] = request.workload_type + if not UtilClient.is_unset(request.workspace_ids): + query['WorkspaceIds'] = request.workspace_ids req = open_api_models.OpenApiRequest( - headers=headers + headers=headers, + query=OpenApiUtilClient.query(query) ) params = open_api_models.Params( - action='ListPermissions', + action='ListQuotaWorkloads', version='2022-01-12', protocol='HTTPS', - pathname=f'/api/v1/permissions', + pathname=f'/api/v1/quotas/{OpenApiUtilClient.get_encode_param(quota_id)}/workloads', method='GET', auth_type='AK', style='ROA', @@ -7408,19 +9306,39 @@ async def list_permissions_with_options_async( body_type='json' ) return TeaCore.from_map( - pai_studio_20220112_models.ListPermissionsResponse(), + pai_studio_20220112_models.ListQuotaWorkloadsResponse(), await self.call_api_async(params, req, runtime) ) - def list_permissions(self) -> pai_studio_20220112_models.ListPermissionsResponse: + def list_quota_workloads( + self, + quota_id: str, + request: pai_studio_20220112_models.ListQuotaWorkloadsRequest, + ) -> pai_studio_20220112_models.ListQuotaWorkloadsResponse: + """ + @summary 您可以通过此API获取Quota上的任务信息 + + @param request: ListQuotaWorkloadsRequest + @return: ListQuotaWorkloadsResponse + """ runtime = util_models.RuntimeOptions() headers = {} - return self.list_permissions_with_options(headers, runtime) + return self.list_quota_workloads_with_options(quota_id, request, headers, runtime) - async def list_permissions_async(self) -> pai_studio_20220112_models.ListPermissionsResponse: + async def list_quota_workloads_async( + self, + quota_id: str, + request: pai_studio_20220112_models.ListQuotaWorkloadsRequest, + ) -> pai_studio_20220112_models.ListQuotaWorkloadsResponse: + """ + @summary 您可以通过此API获取Quota上的任务信息 + + @param request: ListQuotaWorkloadsRequest + @return: ListQuotaWorkloadsResponse + """ runtime = util_models.RuntimeOptions() headers = {} - return await self.list_permissions_with_options_async(headers, runtime) + return await self.list_quota_workloads_with_options_async(quota_id, request, headers, runtime) def list_quotas_with_options( self, @@ -7428,6 +9346,14 @@ def list_quotas_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListQuotasResponse: + """ + @summary 获取Quota列表 + + @param request: ListQuotasRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListQuotasResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.labels): @@ -7452,6 +9378,8 @@ def list_quotas_with_options( query['SortBy'] = request.sort_by if not UtilClient.is_unset(request.statuses): query['Statuses'] = request.statuses + if not UtilClient.is_unset(request.verbose): + query['Verbose'] = request.verbose if not UtilClient.is_unset(request.workspace_ids): query['WorkspaceIds'] = request.workspace_ids req = open_api_models.OpenApiRequest( @@ -7480,6 +9408,14 @@ async def list_quotas_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListQuotasResponse: + """ + @summary 获取Quota列表 + + @param request: ListQuotasRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListQuotasResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.labels): @@ -7504,6 +9440,8 @@ async def list_quotas_with_options_async( query['SortBy'] = request.sort_by if not UtilClient.is_unset(request.statuses): query['Statuses'] = request.statuses + if not UtilClient.is_unset(request.verbose): + query['Verbose'] = request.verbose if not UtilClient.is_unset(request.workspace_ids): query['WorkspaceIds'] = request.workspace_ids req = open_api_models.OpenApiRequest( @@ -7530,6 +9468,12 @@ def list_quotas( self, request: pai_studio_20220112_models.ListQuotasRequest, ) -> pai_studio_20220112_models.ListQuotasResponse: + """ + @summary 获取Quota列表 + + @param request: ListQuotasRequest + @return: ListQuotasResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.list_quotas_with_options(request, headers, runtime) @@ -7538,6 +9482,12 @@ async def list_quotas_async( self, request: pai_studio_20220112_models.ListQuotasRequest, ) -> pai_studio_20220112_models.ListQuotasResponse: + """ + @summary 获取Quota列表 + + @param request: ListQuotasRequest + @return: ListQuotasResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.list_quotas_with_options_async(request, headers, runtime) @@ -7549,6 +9499,14 @@ def list_resource_group_machine_groups_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListResourceGroupMachineGroupsResponse: + """ + @summary list machine groups + + @param request: ListResourceGroupMachineGroupsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListResourceGroupMachineGroupsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.creator_id): @@ -7559,6 +9517,8 @@ def list_resource_group_machine_groups_with_options( query['Name'] = request.name if not UtilClient.is_unset(request.order): query['Order'] = request.order + if not UtilClient.is_unset(request.order_instance_id): + query['OrderInstanceId'] = request.order_instance_id if not UtilClient.is_unset(request.page_number): query['PageNumber'] = request.page_number if not UtilClient.is_unset(request.page_size): @@ -7600,6 +9560,14 @@ async def list_resource_group_machine_groups_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListResourceGroupMachineGroupsResponse: + """ + @summary list machine groups + + @param request: ListResourceGroupMachineGroupsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListResourceGroupMachineGroupsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.creator_id): @@ -7610,6 +9578,8 @@ async def list_resource_group_machine_groups_with_options_async( query['Name'] = request.name if not UtilClient.is_unset(request.order): query['Order'] = request.order + if not UtilClient.is_unset(request.order_instance_id): + query['OrderInstanceId'] = request.order_instance_id if not UtilClient.is_unset(request.page_number): query['PageNumber'] = request.page_number if not UtilClient.is_unset(request.page_size): @@ -7649,6 +9619,12 @@ def list_resource_group_machine_groups( resource_group_id: str, request: pai_studio_20220112_models.ListResourceGroupMachineGroupsRequest, ) -> pai_studio_20220112_models.ListResourceGroupMachineGroupsResponse: + """ + @summary list machine groups + + @param request: ListResourceGroupMachineGroupsRequest + @return: ListResourceGroupMachineGroupsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.list_resource_group_machine_groups_with_options(resource_group_id, request, headers, runtime) @@ -7658,6 +9634,12 @@ async def list_resource_group_machine_groups_async( resource_group_id: str, request: pai_studio_20220112_models.ListResourceGroupMachineGroupsRequest, ) -> pai_studio_20220112_models.ListResourceGroupMachineGroupsResponse: + """ + @summary list machine groups + + @param request: ListResourceGroupMachineGroupsRequest + @return: ListResourceGroupMachineGroupsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.list_resource_group_machine_groups_with_options_async(resource_group_id, request, headers, runtime) @@ -7668,6 +9650,14 @@ def list_resource_groups_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListResourceGroupsResponse: + """ + @summary list resource group + + @param request: ListResourceGroupsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListResourceGroupsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.computing_resource_provider): @@ -7714,6 +9704,14 @@ async def list_resource_groups_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListResourceGroupsResponse: + """ + @summary list resource group + + @param request: ListResourceGroupsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListResourceGroupsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.computing_resource_provider): @@ -7758,6 +9756,12 @@ def list_resource_groups( self, request: pai_studio_20220112_models.ListResourceGroupsRequest, ) -> pai_studio_20220112_models.ListResourceGroupsResponse: + """ + @summary list resource group + + @param request: ListResourceGroupsRequest + @return: ListResourceGroupsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.list_resource_groups_with_options(request, headers, runtime) @@ -7766,6 +9770,12 @@ async def list_resource_groups_async( self, request: pai_studio_20220112_models.ListResourceGroupsRequest, ) -> pai_studio_20220112_models.ListResourceGroupsResponse: + """ + @summary list resource group + + @param request: ListResourceGroupsRequest + @return: ListResourceGroupsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.list_resource_groups_with_options_async(request, headers, runtime) @@ -7776,6 +9786,14 @@ def list_spots_stock_preview_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListSpotsStockPreviewResponse: + """ + @summary 获取多个抢占式实例的库存概览 + + @param request: ListSpotsStockPreviewRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListSpotsStockPreviewResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.instance_types): @@ -7806,6 +9824,14 @@ async def list_spots_stock_preview_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListSpotsStockPreviewResponse: + """ + @summary 获取多个抢占式实例的库存概览 + + @param request: ListSpotsStockPreviewRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListSpotsStockPreviewResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.instance_types): @@ -7834,6 +9860,12 @@ def list_spots_stock_preview( self, request: pai_studio_20220112_models.ListSpotsStockPreviewRequest, ) -> pai_studio_20220112_models.ListSpotsStockPreviewResponse: + """ + @summary 获取多个抢占式实例的库存概览 + + @param request: ListSpotsStockPreviewRequest + @return: ListSpotsStockPreviewResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.list_spots_stock_preview_with_options(request, headers, runtime) @@ -7842,6 +9874,12 @@ async def list_spots_stock_preview_async( self, request: pai_studio_20220112_models.ListSpotsStockPreviewRequest, ) -> pai_studio_20220112_models.ListSpotsStockPreviewResponse: + """ + @summary 获取多个抢占式实例的库存概览 + + @param request: ListSpotsStockPreviewRequest + @return: ListSpotsStockPreviewResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.list_spots_stock_preview_with_options_async(request, headers, runtime) @@ -7852,6 +9890,14 @@ def list_tag_resources_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListTagResourcesResponse: + """ + @summary 查标签接口 + + @param tmp_req: ListTagResourcesRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListTagResourcesResponse + """ UtilClient.validate_model(tmp_req) request = pai_studio_20220112_models.ListTagResourcesShrinkRequest() OpenApiUtilClient.convert(tmp_req, request) @@ -7896,6 +9942,14 @@ async def list_tag_resources_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListTagResourcesResponse: + """ + @summary 查标签接口 + + @param tmp_req: ListTagResourcesRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListTagResourcesResponse + """ UtilClient.validate_model(tmp_req) request = pai_studio_20220112_models.ListTagResourcesShrinkRequest() OpenApiUtilClient.convert(tmp_req, request) @@ -7938,6 +9992,12 @@ def list_tag_resources( self, request: pai_studio_20220112_models.ListTagResourcesRequest, ) -> pai_studio_20220112_models.ListTagResourcesResponse: + """ + @summary 查标签接口 + + @param request: ListTagResourcesRequest + @return: ListTagResourcesResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.list_tag_resources_with_options(request, headers, runtime) @@ -7946,6 +10006,12 @@ async def list_tag_resources_async( self, request: pai_studio_20220112_models.ListTagResourcesRequest, ) -> pai_studio_20220112_models.ListTagResourcesResponse: + """ + @summary 查标签接口 + + @param request: ListTagResourcesRequest + @return: ListTagResourcesResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.list_tag_resources_with_options_async(request, headers, runtime) @@ -7957,6 +10023,14 @@ def list_training_job_events_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListTrainingJobEventsResponse: + """ + @summary 获取指定TrainingJob的事件。 + + @param request: ListTrainingJobEventsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListTrainingJobEventsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.end_time): @@ -7996,6 +10070,14 @@ async def list_training_job_events_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListTrainingJobEventsResponse: + """ + @summary 获取指定TrainingJob的事件。 + + @param request: ListTrainingJobEventsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListTrainingJobEventsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.end_time): @@ -8033,6 +10115,12 @@ def list_training_job_events( training_job_id: str, request: pai_studio_20220112_models.ListTrainingJobEventsRequest, ) -> pai_studio_20220112_models.ListTrainingJobEventsResponse: + """ + @summary 获取指定TrainingJob的事件。 + + @param request: ListTrainingJobEventsRequest + @return: ListTrainingJobEventsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.list_training_job_events_with_options(training_job_id, request, headers, runtime) @@ -8042,6 +10130,12 @@ async def list_training_job_events_async( training_job_id: str, request: pai_studio_20220112_models.ListTrainingJobEventsRequest, ) -> pai_studio_20220112_models.ListTrainingJobEventsResponse: + """ + @summary 获取指定TrainingJob的事件。 + + @param request: ListTrainingJobEventsRequest + @return: ListTrainingJobEventsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.list_training_job_events_with_options_async(training_job_id, request, headers, runtime) @@ -8054,6 +10148,14 @@ def list_training_job_instance_events_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListTrainingJobInstanceEventsResponse: + """ + @summary 获取指定Instance(TrainingJob的运行单元)的日志。 + + @param request: ListTrainingJobInstanceEventsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListTrainingJobInstanceEventsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.end_time): @@ -8094,6 +10196,14 @@ async def list_training_job_instance_events_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListTrainingJobInstanceEventsResponse: + """ + @summary 获取指定Instance(TrainingJob的运行单元)的日志。 + + @param request: ListTrainingJobInstanceEventsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListTrainingJobInstanceEventsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.end_time): @@ -8132,6 +10242,12 @@ def list_training_job_instance_events( instance_id: str, request: pai_studio_20220112_models.ListTrainingJobInstanceEventsRequest, ) -> pai_studio_20220112_models.ListTrainingJobInstanceEventsResponse: + """ + @summary 获取指定Instance(TrainingJob的运行单元)的日志。 + + @param request: ListTrainingJobInstanceEventsRequest + @return: ListTrainingJobInstanceEventsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.list_training_job_instance_events_with_options(training_job_id, instance_id, request, headers, runtime) @@ -8142,6 +10258,12 @@ async def list_training_job_instance_events_async( instance_id: str, request: pai_studio_20220112_models.ListTrainingJobInstanceEventsRequest, ) -> pai_studio_20220112_models.ListTrainingJobInstanceEventsResponse: + """ + @summary 获取指定Instance(TrainingJob的运行单元)的日志。 + + @param request: ListTrainingJobInstanceEventsRequest + @return: ListTrainingJobInstanceEventsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.list_training_job_instance_events_with_options_async(training_job_id, instance_id, request, headers, runtime) @@ -8153,6 +10275,14 @@ def list_training_job_instance_metrics_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListTrainingJobInstanceMetricsResponse: + """ + @summary 获取Training Job实例的Metrics + + @param request: ListTrainingJobInstanceMetricsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListTrainingJobInstanceMetricsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.end_time): @@ -8194,6 +10324,14 @@ async def list_training_job_instance_metrics_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListTrainingJobInstanceMetricsResponse: + """ + @summary 获取Training Job实例的Metrics + + @param request: ListTrainingJobInstanceMetricsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListTrainingJobInstanceMetricsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.end_time): @@ -8233,6 +10371,12 @@ def list_training_job_instance_metrics( training_job_id: str, request: pai_studio_20220112_models.ListTrainingJobInstanceMetricsRequest, ) -> pai_studio_20220112_models.ListTrainingJobInstanceMetricsResponse: + """ + @summary 获取Training Job实例的Metrics + + @param request: ListTrainingJobInstanceMetricsRequest + @return: ListTrainingJobInstanceMetricsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.list_training_job_instance_metrics_with_options(training_job_id, request, headers, runtime) @@ -8242,6 +10386,12 @@ async def list_training_job_instance_metrics_async( training_job_id: str, request: pai_studio_20220112_models.ListTrainingJobInstanceMetricsRequest, ) -> pai_studio_20220112_models.ListTrainingJobInstanceMetricsResponse: + """ + @summary 获取Training Job实例的Metrics + + @param request: ListTrainingJobInstanceMetricsRequest + @return: ListTrainingJobInstanceMetricsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.list_training_job_instance_metrics_with_options_async(training_job_id, request, headers, runtime) @@ -8253,6 +10403,14 @@ def list_training_job_logs_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListTrainingJobLogsResponse: + """ + @summary 获取Training Job的日志 + + @param request: ListTrainingJobLogsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListTrainingJobLogsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.end_time): @@ -8296,6 +10454,14 @@ async def list_training_job_logs_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListTrainingJobLogsResponse: + """ + @summary 获取Training Job的日志 + + @param request: ListTrainingJobLogsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListTrainingJobLogsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.end_time): @@ -8337,6 +10503,12 @@ def list_training_job_logs( training_job_id: str, request: pai_studio_20220112_models.ListTrainingJobLogsRequest, ) -> pai_studio_20220112_models.ListTrainingJobLogsResponse: + """ + @summary 获取Training Job的日志 + + @param request: ListTrainingJobLogsRequest + @return: ListTrainingJobLogsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.list_training_job_logs_with_options(training_job_id, request, headers, runtime) @@ -8346,6 +10518,12 @@ async def list_training_job_logs_async( training_job_id: str, request: pai_studio_20220112_models.ListTrainingJobLogsRequest, ) -> pai_studio_20220112_models.ListTrainingJobLogsResponse: + """ + @summary 获取Training Job的日志 + + @param request: ListTrainingJobLogsRequest + @return: ListTrainingJobLogsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.list_training_job_logs_with_options_async(training_job_id, request, headers, runtime) @@ -8357,6 +10535,14 @@ def list_training_job_metrics_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListTrainingJobMetricsResponse: + """ + @summary 获取Training Job的Metrics + + @param request: ListTrainingJobMetricsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListTrainingJobMetricsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.end_time): @@ -8400,6 +10586,14 @@ async def list_training_job_metrics_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListTrainingJobMetricsResponse: + """ + @summary 获取Training Job的Metrics + + @param request: ListTrainingJobMetricsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListTrainingJobMetricsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.end_time): @@ -8441,6 +10635,12 @@ def list_training_job_metrics( training_job_id: str, request: pai_studio_20220112_models.ListTrainingJobMetricsRequest, ) -> pai_studio_20220112_models.ListTrainingJobMetricsResponse: + """ + @summary 获取Training Job的Metrics + + @param request: ListTrainingJobMetricsRequest + @return: ListTrainingJobMetricsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.list_training_job_metrics_with_options(training_job_id, request, headers, runtime) @@ -8450,6 +10650,12 @@ async def list_training_job_metrics_async( training_job_id: str, request: pai_studio_20220112_models.ListTrainingJobMetricsRequest, ) -> pai_studio_20220112_models.ListTrainingJobMetricsResponse: + """ + @summary 获取Training Job的Metrics + + @param request: ListTrainingJobMetricsRequest + @return: ListTrainingJobMetricsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.list_training_job_metrics_with_options_async(training_job_id, request, headers, runtime) @@ -8461,6 +10667,14 @@ def list_training_job_output_models_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListTrainingJobOutputModelsResponse: + """ + @summary 获取Training Job 产出的所有模型信息 + + @param request: ListTrainingJobOutputModelsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListTrainingJobOutputModelsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.token): @@ -8492,6 +10706,14 @@ async def list_training_job_output_models_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListTrainingJobOutputModelsResponse: + """ + @summary 获取Training Job 产出的所有模型信息 + + @param request: ListTrainingJobOutputModelsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListTrainingJobOutputModelsResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.token): @@ -8521,6 +10743,12 @@ def list_training_job_output_models( training_job_id: str, request: pai_studio_20220112_models.ListTrainingJobOutputModelsRequest, ) -> pai_studio_20220112_models.ListTrainingJobOutputModelsResponse: + """ + @summary 获取Training Job 产出的所有模型信息 + + @param request: ListTrainingJobOutputModelsRequest + @return: ListTrainingJobOutputModelsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.list_training_job_output_models_with_options(training_job_id, request, headers, runtime) @@ -8530,6 +10758,12 @@ async def list_training_job_output_models_async( training_job_id: str, request: pai_studio_20220112_models.ListTrainingJobOutputModelsRequest, ) -> pai_studio_20220112_models.ListTrainingJobOutputModelsResponse: + """ + @summary 获取Training Job 产出的所有模型信息 + + @param request: ListTrainingJobOutputModelsRequest + @return: ListTrainingJobOutputModelsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.list_training_job_output_models_with_options_async(training_job_id, request, headers, runtime) @@ -8540,6 +10774,14 @@ def list_training_jobs_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListTrainingJobsResponse: + """ + @summary 获取TrainingJob的列表 + + @param tmp_req: ListTrainingJobsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListTrainingJobsResponse + """ UtilClient.validate_model(tmp_req) request = pai_studio_20220112_models.ListTrainingJobsShrinkRequest() OpenApiUtilClient.convert(tmp_req, request) @@ -8600,6 +10842,14 @@ async def list_training_jobs_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ListTrainingJobsResponse: + """ + @summary 获取TrainingJob的列表 + + @param tmp_req: ListTrainingJobsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ListTrainingJobsResponse + """ UtilClient.validate_model(tmp_req) request = pai_studio_20220112_models.ListTrainingJobsShrinkRequest() OpenApiUtilClient.convert(tmp_req, request) @@ -8658,6 +10908,12 @@ def list_training_jobs( self, request: pai_studio_20220112_models.ListTrainingJobsRequest, ) -> pai_studio_20220112_models.ListTrainingJobsResponse: + """ + @summary 获取TrainingJob的列表 + + @param request: ListTrainingJobsRequest + @return: ListTrainingJobsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.list_training_jobs_with_options(request, headers, runtime) @@ -8666,6 +10922,12 @@ async def list_training_jobs_async( self, request: pai_studio_20220112_models.ListTrainingJobsRequest, ) -> pai_studio_20220112_models.ListTrainingJobsResponse: + """ + @summary 获取TrainingJob的列表 + + @param request: ListTrainingJobsRequest + @return: ListTrainingJobsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.list_training_jobs_with_options_async(request, headers, runtime) @@ -8677,6 +10939,14 @@ def operate_node_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.OperateNodeResponse: + """ + @summary 您可以通过OperateNode对节点进行操作 + + @param request: OperateNodeRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: OperateNodeResponse + """ UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.operation): @@ -8710,6 +10980,14 @@ async def operate_node_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.OperateNodeResponse: + """ + @summary 您可以通过OperateNode对节点进行操作 + + @param request: OperateNodeRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: OperateNodeResponse + """ UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.operation): @@ -8741,6 +11019,12 @@ def operate_node( node_id: str, request: pai_studio_20220112_models.OperateNodeRequest, ) -> pai_studio_20220112_models.OperateNodeResponse: + """ + @summary 您可以通过OperateNode对节点进行操作 + + @param request: OperateNodeRequest + @return: OperateNodeResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.operate_node_with_options(node_id, request, headers, runtime) @@ -8750,6 +11034,12 @@ async def operate_node_async( node_id: str, request: pai_studio_20220112_models.OperateNodeRequest, ) -> pai_studio_20220112_models.OperateNodeResponse: + """ + @summary 您可以通过OperateNode对节点进行操作 + + @param request: OperateNodeRequest + @return: OperateNodeResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.operate_node_with_options_async(node_id, request, headers, runtime) @@ -8761,6 +11051,14 @@ def release_algorithm_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ReleaseAlgorithmResponse: + """ + @summary 发布算法为公共算法 + + @param request: ReleaseAlgorithmRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ReleaseAlgorithmResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.target_algorithm_name): @@ -8794,6 +11092,14 @@ async def release_algorithm_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ReleaseAlgorithmResponse: + """ + @summary 发布算法为公共算法 + + @param request: ReleaseAlgorithmRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ReleaseAlgorithmResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.target_algorithm_name): @@ -8825,6 +11131,12 @@ def release_algorithm( algorithm_id: str, request: pai_studio_20220112_models.ReleaseAlgorithmRequest, ) -> pai_studio_20220112_models.ReleaseAlgorithmResponse: + """ + @summary 发布算法为公共算法 + + @param request: ReleaseAlgorithmRequest + @return: ReleaseAlgorithmResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.release_algorithm_with_options(algorithm_id, request, headers, runtime) @@ -8834,6 +11146,12 @@ async def release_algorithm_async( algorithm_id: str, request: pai_studio_20220112_models.ReleaseAlgorithmRequest, ) -> pai_studio_20220112_models.ReleaseAlgorithmResponse: + """ + @summary 发布算法为公共算法 + + @param request: ReleaseAlgorithmRequest + @return: ReleaseAlgorithmResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.release_algorithm_with_options_async(algorithm_id, request, headers, runtime) @@ -8846,6 +11164,14 @@ def release_algorithm_version_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ReleaseAlgorithmVersionResponse: + """ + @summary 发布公共算法版本 + + @param request: ReleaseAlgorithmVersionRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ReleaseAlgorithmVersionResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.target_algorithm_name): @@ -8882,6 +11208,14 @@ async def release_algorithm_version_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ReleaseAlgorithmVersionResponse: + """ + @summary 发布公共算法版本 + + @param request: ReleaseAlgorithmVersionRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ReleaseAlgorithmVersionResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.target_algorithm_name): @@ -8916,6 +11250,12 @@ def release_algorithm_version( algorithm_version: str, request: pai_studio_20220112_models.ReleaseAlgorithmVersionRequest, ) -> pai_studio_20220112_models.ReleaseAlgorithmVersionResponse: + """ + @summary 发布公共算法版本 + + @param request: ReleaseAlgorithmVersionRequest + @return: ReleaseAlgorithmVersionResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.release_algorithm_version_with_options(algorithm_id, algorithm_version, request, headers, runtime) @@ -8926,10 +11266,110 @@ async def release_algorithm_version_async( algorithm_version: str, request: pai_studio_20220112_models.ReleaseAlgorithmVersionRequest, ) -> pai_studio_20220112_models.ReleaseAlgorithmVersionResponse: + """ + @summary 发布公共算法版本 + + @param request: ReleaseAlgorithmVersionRequest + @return: ReleaseAlgorithmVersionResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.release_algorithm_version_with_options_async(algorithm_id, algorithm_version, request, headers, runtime) + def release_machine_group_with_options( + self, + resource_group_id: str, + machine_group_id: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.ReleaseMachineGroupResponse: + """ + @summary 释放到期的机器组 + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ReleaseMachineGroupResponse + """ + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='ReleaseMachineGroup', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/resources/{OpenApiUtilClient.get_encode_param(resource_group_id)}/machinegroups/{OpenApiUtilClient.get_encode_param(machine_group_id)}', + method='POST', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.ReleaseMachineGroupResponse(), + self.call_api(params, req, runtime) + ) + + async def release_machine_group_with_options_async( + self, + resource_group_id: str, + machine_group_id: str, + headers: Dict[str, str], + runtime: util_models.RuntimeOptions, + ) -> pai_studio_20220112_models.ReleaseMachineGroupResponse: + """ + @summary 释放到期的机器组 + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ReleaseMachineGroupResponse + """ + req = open_api_models.OpenApiRequest( + headers=headers + ) + params = open_api_models.Params( + action='ReleaseMachineGroup', + version='2022-01-12', + protocol='HTTPS', + pathname=f'/api/v1/resources/{OpenApiUtilClient.get_encode_param(resource_group_id)}/machinegroups/{OpenApiUtilClient.get_encode_param(machine_group_id)}', + method='POST', + auth_type='AK', + style='ROA', + req_body_type='json', + body_type='json' + ) + return TeaCore.from_map( + pai_studio_20220112_models.ReleaseMachineGroupResponse(), + await self.call_api_async(params, req, runtime) + ) + + def release_machine_group( + self, + resource_group_id: str, + machine_group_id: str, + ) -> pai_studio_20220112_models.ReleaseMachineGroupResponse: + """ + @summary 释放到期的机器组 + + @return: ReleaseMachineGroupResponse + """ + runtime = util_models.RuntimeOptions() + headers = {} + return self.release_machine_group_with_options(resource_group_id, machine_group_id, headers, runtime) + + async def release_machine_group_async( + self, + resource_group_id: str, + machine_group_id: str, + ) -> pai_studio_20220112_models.ReleaseMachineGroupResponse: + """ + @summary 释放到期的机器组 + + @return: ReleaseMachineGroupResponse + """ + runtime = util_models.RuntimeOptions() + headers = {} + return await self.release_machine_group_with_options_async(resource_group_id, machine_group_id, headers, runtime) + def scale_quota_with_options( self, quota_id: str, @@ -8937,6 +11377,14 @@ def scale_quota_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ScaleQuotaResponse: + """ + @summary 扩缩容Quota + + @param request: ScaleQuotaRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ScaleQuotaResponse + """ UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.min): @@ -8970,6 +11418,14 @@ async def scale_quota_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.ScaleQuotaResponse: + """ + @summary 扩缩容Quota + + @param request: ScaleQuotaRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: ScaleQuotaResponse + """ UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.min): @@ -9001,6 +11457,12 @@ def scale_quota( quota_id: str, request: pai_studio_20220112_models.ScaleQuotaRequest, ) -> pai_studio_20220112_models.ScaleQuotaResponse: + """ + @summary 扩缩容Quota + + @param request: ScaleQuotaRequest + @return: ScaleQuotaResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.scale_quota_with_options(quota_id, request, headers, runtime) @@ -9010,6 +11472,12 @@ async def scale_quota_async( quota_id: str, request: pai_studio_20220112_models.ScaleQuotaRequest, ) -> pai_studio_20220112_models.ScaleQuotaResponse: + """ + @summary 扩缩容Quota + + @param request: ScaleQuotaRequest + @return: ScaleQuotaResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.scale_quota_with_options_async(quota_id, request, headers, runtime) @@ -9020,6 +11488,13 @@ def stop_training_job_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.StopTrainingJobResponse: + """ + @summary 停止一个TrainingJob + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: StopTrainingJobResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -9045,6 +11520,13 @@ async def stop_training_job_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.StopTrainingJobResponse: + """ + @summary 停止一个TrainingJob + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: StopTrainingJobResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -9068,6 +11550,11 @@ def stop_training_job( self, training_job_id: str, ) -> pai_studio_20220112_models.StopTrainingJobResponse: + """ + @summary 停止一个TrainingJob + + @return: StopTrainingJobResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.stop_training_job_with_options(training_job_id, headers, runtime) @@ -9076,6 +11563,11 @@ async def stop_training_job_async( self, training_job_id: str, ) -> pai_studio_20220112_models.StopTrainingJobResponse: + """ + @summary 停止一个TrainingJob + + @return: StopTrainingJobResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.stop_training_job_with_options_async(training_job_id, headers, runtime) @@ -9086,6 +11578,14 @@ def tag_resources_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.TagResourcesResponse: + """ + @summary 打标签接口 + + @param request: TagResourcesRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: TagResourcesResponse + """ UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.region_id): @@ -9122,6 +11622,14 @@ async def tag_resources_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.TagResourcesResponse: + """ + @summary 打标签接口 + + @param request: TagResourcesRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: TagResourcesResponse + """ UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.region_id): @@ -9156,6 +11664,12 @@ def tag_resources( self, request: pai_studio_20220112_models.TagResourcesRequest, ) -> pai_studio_20220112_models.TagResourcesResponse: + """ + @summary 打标签接口 + + @param request: TagResourcesRequest + @return: TagResourcesResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.tag_resources_with_options(request, headers, runtime) @@ -9164,6 +11678,12 @@ async def tag_resources_async( self, request: pai_studio_20220112_models.TagResourcesRequest, ) -> pai_studio_20220112_models.TagResourcesResponse: + """ + @summary 打标签接口 + + @param request: TagResourcesRequest + @return: TagResourcesResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.tag_resources_with_options_async(request, headers, runtime) @@ -9174,6 +11694,14 @@ def untag_resources_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.UntagResourcesResponse: + """ + @summary 删标签接口 + + @param tmp_req: UntagResourcesRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: UntagResourcesResponse + """ UtilClient.validate_model(tmp_req) request = pai_studio_20220112_models.UntagResourcesShrinkRequest() OpenApiUtilClient.convert(tmp_req, request) @@ -9218,6 +11746,14 @@ async def untag_resources_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.UntagResourcesResponse: + """ + @summary 删标签接口 + + @param tmp_req: UntagResourcesRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: UntagResourcesResponse + """ UtilClient.validate_model(tmp_req) request = pai_studio_20220112_models.UntagResourcesShrinkRequest() OpenApiUtilClient.convert(tmp_req, request) @@ -9260,6 +11796,12 @@ def untag_resources( self, request: pai_studio_20220112_models.UntagResourcesRequest, ) -> pai_studio_20220112_models.UntagResourcesResponse: + """ + @summary 删标签接口 + + @param request: UntagResourcesRequest + @return: UntagResourcesResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.untag_resources_with_options(request, headers, runtime) @@ -9268,6 +11810,12 @@ async def untag_resources_async( self, request: pai_studio_20220112_models.UntagResourcesRequest, ) -> pai_studio_20220112_models.UntagResourcesResponse: + """ + @summary 删标签接口 + + @param request: UntagResourcesRequest + @return: UntagResourcesResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.untag_resources_with_options_async(request, headers, runtime) @@ -9279,6 +11827,14 @@ def update_algorithm_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.UpdateAlgorithmResponse: + """ + @summary 更新算法 + + @param request: UpdateAlgorithmRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: UpdateAlgorithmResponse + """ UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.algorithm_description): @@ -9312,6 +11868,14 @@ async def update_algorithm_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.UpdateAlgorithmResponse: + """ + @summary 更新算法 + + @param request: UpdateAlgorithmRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: UpdateAlgorithmResponse + """ UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.algorithm_description): @@ -9343,6 +11907,12 @@ def update_algorithm( algorithm_id: str, request: pai_studio_20220112_models.UpdateAlgorithmRequest, ) -> pai_studio_20220112_models.UpdateAlgorithmResponse: + """ + @summary 更新算法 + + @param request: UpdateAlgorithmRequest + @return: UpdateAlgorithmResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.update_algorithm_with_options(algorithm_id, request, headers, runtime) @@ -9352,6 +11922,12 @@ async def update_algorithm_async( algorithm_id: str, request: pai_studio_20220112_models.UpdateAlgorithmRequest, ) -> pai_studio_20220112_models.UpdateAlgorithmResponse: + """ + @summary 更新算法 + + @param request: UpdateAlgorithmRequest + @return: UpdateAlgorithmResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.update_algorithm_with_options_async(algorithm_id, request, headers, runtime) @@ -9364,6 +11940,14 @@ def update_algorithm_version_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.UpdateAlgorithmVersionResponse: + """ + @summary 更新算法 + + @param tmp_req: UpdateAlgorithmVersionRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: UpdateAlgorithmVersionResponse + """ UtilClient.validate_model(tmp_req) request = pai_studio_20220112_models.UpdateAlgorithmVersionShrinkRequest() OpenApiUtilClient.convert(tmp_req, request) @@ -9400,6 +11984,14 @@ async def update_algorithm_version_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.UpdateAlgorithmVersionResponse: + """ + @summary 更新算法 + + @param tmp_req: UpdateAlgorithmVersionRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: UpdateAlgorithmVersionResponse + """ UtilClient.validate_model(tmp_req) request = pai_studio_20220112_models.UpdateAlgorithmVersionShrinkRequest() OpenApiUtilClient.convert(tmp_req, request) @@ -9434,6 +12026,12 @@ def update_algorithm_version( algorithm_version: str, request: pai_studio_20220112_models.UpdateAlgorithmVersionRequest, ) -> pai_studio_20220112_models.UpdateAlgorithmVersionResponse: + """ + @summary 更新算法 + + @param request: UpdateAlgorithmVersionRequest + @return: UpdateAlgorithmVersionResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.update_algorithm_version_with_options(algorithm_id, algorithm_version, request, headers, runtime) @@ -9444,6 +12042,12 @@ async def update_algorithm_version_async( algorithm_version: str, request: pai_studio_20220112_models.UpdateAlgorithmVersionRequest, ) -> pai_studio_20220112_models.UpdateAlgorithmVersionResponse: + """ + @summary 更新算法 + + @param request: UpdateAlgorithmVersionRequest + @return: UpdateAlgorithmVersionResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.update_algorithm_version_with_options_async(algorithm_id, algorithm_version, request, headers, runtime) @@ -9455,6 +12059,14 @@ def update_component_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.UpdateComponentResponse: + """ + @summary 更新组件 + + @param request: UpdateComponentRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: UpdateComponentResponse + """ UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.description): @@ -9490,6 +12102,14 @@ async def update_component_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.UpdateComponentResponse: + """ + @summary 更新组件 + + @param request: UpdateComponentRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: UpdateComponentResponse + """ UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.description): @@ -9523,6 +12143,12 @@ def update_component( component_id: str, request: pai_studio_20220112_models.UpdateComponentRequest, ) -> pai_studio_20220112_models.UpdateComponentResponse: + """ + @summary 更新组件 + + @param request: UpdateComponentRequest + @return: UpdateComponentResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.update_component_with_options(component_id, request, headers, runtime) @@ -9532,6 +12158,12 @@ async def update_component_async( component_id: str, request: pai_studio_20220112_models.UpdateComponentRequest, ) -> pai_studio_20220112_models.UpdateComponentResponse: + """ + @summary 更新组件 + + @param request: UpdateComponentRequest + @return: UpdateComponentResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.update_component_with_options_async(component_id, request, headers, runtime) @@ -9544,6 +12176,14 @@ def update_component_version_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.UpdateComponentVersionResponse: + """ + @summary 更新组件版本 + + @param request: UpdateComponentVersionRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: UpdateComponentVersionResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.labels): @@ -9578,6 +12218,14 @@ async def update_component_version_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.UpdateComponentVersionResponse: + """ + @summary 更新组件版本 + + @param request: UpdateComponentVersionRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: UpdateComponentVersionResponse + """ UtilClient.validate_model(request) query = {} if not UtilClient.is_unset(request.labels): @@ -9610,6 +12258,12 @@ def update_component_version( version: str, request: pai_studio_20220112_models.UpdateComponentVersionRequest, ) -> pai_studio_20220112_models.UpdateComponentVersionResponse: + """ + @summary 更新组件版本 + + @param request: UpdateComponentVersionRequest + @return: UpdateComponentVersionResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.update_component_version_with_options(component_id, version, request, headers, runtime) @@ -9620,6 +12274,12 @@ async def update_component_version_async( version: str, request: pai_studio_20220112_models.UpdateComponentVersionRequest, ) -> pai_studio_20220112_models.UpdateComponentVersionResponse: + """ + @summary 更新组件版本 + + @param request: UpdateComponentVersionRequest + @return: UpdateComponentVersionResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.update_component_version_with_options_async(component_id, version, request, headers, runtime) @@ -9630,6 +12290,13 @@ def update_component_version_snapshot_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.UpdateComponentVersionSnapshotResponse: + """ + @summary 更新组件版本快照 + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: UpdateComponentVersionSnapshotResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -9655,6 +12322,13 @@ async def update_component_version_snapshot_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.UpdateComponentVersionSnapshotResponse: + """ + @summary 更新组件版本快照 + + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: UpdateComponentVersionSnapshotResponse + """ req = open_api_models.OpenApiRequest( headers=headers ) @@ -9678,6 +12352,11 @@ def update_component_version_snapshot( self, snapshot_id: str, ) -> pai_studio_20220112_models.UpdateComponentVersionSnapshotResponse: + """ + @summary 更新组件版本快照 + + @return: UpdateComponentVersionSnapshotResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.update_component_version_snapshot_with_options(snapshot_id, headers, runtime) @@ -9686,106 +12365,15 @@ async def update_component_version_snapshot_async( self, snapshot_id: str, ) -> pai_studio_20220112_models.UpdateComponentVersionSnapshotResponse: + """ + @summary 更新组件版本快照 + + @return: UpdateComponentVersionSnapshotResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.update_component_version_snapshot_with_options_async(snapshot_id, headers, runtime) - def update_llmproject_with_options( - self, - project_id: str, - request: pai_studio_20220112_models.UpdateLLMProjectRequest, - headers: Dict[str, str], - runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.UpdateLLMProjectResponse: - UtilClient.validate_model(request) - body = {} - if not UtilClient.is_unset(request.labels): - body['Labels'] = request.labels - if not UtilClient.is_unset(request.project_description): - body['ProjectDescription'] = request.project_description - if not UtilClient.is_unset(request.project_name): - body['ProjectName'] = request.project_name - if not UtilClient.is_unset(request.root_path): - body['RootPath'] = request.root_path - if not UtilClient.is_unset(request.runtime): - body['Runtime'] = request.runtime - req = open_api_models.OpenApiRequest( - headers=headers, - body=OpenApiUtilClient.parse_to_map(body) - ) - params = open_api_models.Params( - action='UpdateLLMProject', - version='2022-01-12', - protocol='HTTPS', - pathname=f'/api/v1/langstudio/projects/{OpenApiUtilClient.get_encode_param(project_id)}', - method='PUT', - auth_type='AK', - style='ROA', - req_body_type='json', - body_type='json' - ) - return TeaCore.from_map( - pai_studio_20220112_models.UpdateLLMProjectResponse(), - self.call_api(params, req, runtime) - ) - - async def update_llmproject_with_options_async( - self, - project_id: str, - request: pai_studio_20220112_models.UpdateLLMProjectRequest, - headers: Dict[str, str], - runtime: util_models.RuntimeOptions, - ) -> pai_studio_20220112_models.UpdateLLMProjectResponse: - UtilClient.validate_model(request) - body = {} - if not UtilClient.is_unset(request.labels): - body['Labels'] = request.labels - if not UtilClient.is_unset(request.project_description): - body['ProjectDescription'] = request.project_description - if not UtilClient.is_unset(request.project_name): - body['ProjectName'] = request.project_name - if not UtilClient.is_unset(request.root_path): - body['RootPath'] = request.root_path - if not UtilClient.is_unset(request.runtime): - body['Runtime'] = request.runtime - req = open_api_models.OpenApiRequest( - headers=headers, - body=OpenApiUtilClient.parse_to_map(body) - ) - params = open_api_models.Params( - action='UpdateLLMProject', - version='2022-01-12', - protocol='HTTPS', - pathname=f'/api/v1/langstudio/projects/{OpenApiUtilClient.get_encode_param(project_id)}', - method='PUT', - auth_type='AK', - style='ROA', - req_body_type='json', - body_type='json' - ) - return TeaCore.from_map( - pai_studio_20220112_models.UpdateLLMProjectResponse(), - await self.call_api_async(params, req, runtime) - ) - - def update_llmproject( - self, - project_id: str, - request: pai_studio_20220112_models.UpdateLLMProjectRequest, - ) -> pai_studio_20220112_models.UpdateLLMProjectResponse: - runtime = util_models.RuntimeOptions() - headers = {} - return self.update_llmproject_with_options(project_id, request, headers, runtime) - - async def update_llmproject_async( - self, - project_id: str, - request: pai_studio_20220112_models.UpdateLLMProjectRequest, - ) -> pai_studio_20220112_models.UpdateLLMProjectResponse: - runtime = util_models.RuntimeOptions() - headers = {} - return await self.update_llmproject_with_options_async(project_id, request, headers, runtime) - def update_quota_with_options( self, quota_id: str, @@ -9793,6 +12381,14 @@ def update_quota_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.UpdateQuotaResponse: + """ + @summary 更新Quota + + @param request: UpdateQuotaRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: UpdateQuotaResponse + """ UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.description): @@ -9801,6 +12397,10 @@ def update_quota_with_options( body['Labels'] = request.labels if not UtilClient.is_unset(request.queue_strategy): body['QueueStrategy'] = request.queue_strategy + if not UtilClient.is_unset(request.quota_config): + body['QuotaConfig'] = request.quota_config + if not UtilClient.is_unset(request.quota_name): + body['QuotaName'] = request.quota_name req = open_api_models.OpenApiRequest( headers=headers, body=OpenApiUtilClient.parse_to_map(body) @@ -9828,6 +12428,14 @@ async def update_quota_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.UpdateQuotaResponse: + """ + @summary 更新Quota + + @param request: UpdateQuotaRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: UpdateQuotaResponse + """ UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.description): @@ -9836,6 +12444,10 @@ async def update_quota_with_options_async( body['Labels'] = request.labels if not UtilClient.is_unset(request.queue_strategy): body['QueueStrategy'] = request.queue_strategy + if not UtilClient.is_unset(request.quota_config): + body['QuotaConfig'] = request.quota_config + if not UtilClient.is_unset(request.quota_name): + body['QuotaName'] = request.quota_name req = open_api_models.OpenApiRequest( headers=headers, body=OpenApiUtilClient.parse_to_map(body) @@ -9861,6 +12473,12 @@ def update_quota( quota_id: str, request: pai_studio_20220112_models.UpdateQuotaRequest, ) -> pai_studio_20220112_models.UpdateQuotaResponse: + """ + @summary 更新Quota + + @param request: UpdateQuotaRequest + @return: UpdateQuotaResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.update_quota_with_options(quota_id, request, headers, runtime) @@ -9870,6 +12488,12 @@ async def update_quota_async( quota_id: str, request: pai_studio_20220112_models.UpdateQuotaRequest, ) -> pai_studio_20220112_models.UpdateQuotaResponse: + """ + @summary 更新Quota + + @param request: UpdateQuotaRequest + @return: UpdateQuotaResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.update_quota_with_options_async(quota_id, request, headers, runtime) @@ -9881,6 +12505,14 @@ def update_quota_labels_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.UpdateQuotaLabelsResponse: + """ + @summary 更新Quota标签 + + @param request: UpdateQuotaLabelsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: UpdateQuotaLabelsResponse + """ UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.labels): @@ -9912,6 +12544,14 @@ async def update_quota_labels_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.UpdateQuotaLabelsResponse: + """ + @summary 更新Quota标签 + + @param request: UpdateQuotaLabelsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: UpdateQuotaLabelsResponse + """ UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.labels): @@ -9941,6 +12581,12 @@ def update_quota_labels( quota_id: str, request: pai_studio_20220112_models.UpdateQuotaLabelsRequest, ) -> pai_studio_20220112_models.UpdateQuotaLabelsResponse: + """ + @summary 更新Quota标签 + + @param request: UpdateQuotaLabelsRequest + @return: UpdateQuotaLabelsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.update_quota_labels_with_options(quota_id, request, headers, runtime) @@ -9950,6 +12596,12 @@ async def update_quota_labels_async( quota_id: str, request: pai_studio_20220112_models.UpdateQuotaLabelsRequest, ) -> pai_studio_20220112_models.UpdateQuotaLabelsResponse: + """ + @summary 更新Quota标签 + + @param request: UpdateQuotaLabelsRequest + @return: UpdateQuotaLabelsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.update_quota_labels_with_options_async(quota_id, request, headers, runtime) @@ -9961,6 +12613,14 @@ def update_resource_group_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.UpdateResourceGroupResponse: + """ + @summary 更新Resource Group + + @param request: UpdateResourceGroupRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: UpdateResourceGroupResponse + """ UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.description): @@ -9998,6 +12658,14 @@ async def update_resource_group_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.UpdateResourceGroupResponse: + """ + @summary 更新Resource Group + + @param request: UpdateResourceGroupRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: UpdateResourceGroupResponse + """ UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.description): @@ -10033,6 +12701,12 @@ def update_resource_group( resource_group_id: str, request: pai_studio_20220112_models.UpdateResourceGroupRequest, ) -> pai_studio_20220112_models.UpdateResourceGroupResponse: + """ + @summary 更新Resource Group + + @param request: UpdateResourceGroupRequest + @return: UpdateResourceGroupResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.update_resource_group_with_options(resource_group_id, request, headers, runtime) @@ -10042,6 +12716,12 @@ async def update_resource_group_async( resource_group_id: str, request: pai_studio_20220112_models.UpdateResourceGroupRequest, ) -> pai_studio_20220112_models.UpdateResourceGroupResponse: + """ + @summary 更新Resource Group + + @param request: UpdateResourceGroupRequest + @return: UpdateResourceGroupResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.update_resource_group_with_options_async(resource_group_id, request, headers, runtime) @@ -10054,6 +12734,14 @@ def update_resource_group_machine_group_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.UpdateResourceGroupMachineGroupResponse: + """ + @summary 更新Machine Group + + @param request: UpdateResourceGroupMachineGroupRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: UpdateResourceGroupMachineGroupResponse + """ UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.name): @@ -10086,6 +12774,14 @@ async def update_resource_group_machine_group_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.UpdateResourceGroupMachineGroupResponse: + """ + @summary 更新Machine Group + + @param request: UpdateResourceGroupMachineGroupRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: UpdateResourceGroupMachineGroupResponse + """ UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.name): @@ -10116,6 +12812,12 @@ def update_resource_group_machine_group( machine_group_id: str, request: pai_studio_20220112_models.UpdateResourceGroupMachineGroupRequest, ) -> pai_studio_20220112_models.UpdateResourceGroupMachineGroupResponse: + """ + @summary 更新Machine Group + + @param request: UpdateResourceGroupMachineGroupRequest + @return: UpdateResourceGroupMachineGroupResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.update_resource_group_machine_group_with_options(resource_group_id, machine_group_id, request, headers, runtime) @@ -10126,6 +12828,12 @@ async def update_resource_group_machine_group_async( machine_group_id: str, request: pai_studio_20220112_models.UpdateResourceGroupMachineGroupRequest, ) -> pai_studio_20220112_models.UpdateResourceGroupMachineGroupResponse: + """ + @summary 更新Machine Group + + @param request: UpdateResourceGroupMachineGroupRequest + @return: UpdateResourceGroupMachineGroupResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.update_resource_group_machine_group_with_options_async(resource_group_id, machine_group_id, request, headers, runtime) @@ -10137,6 +12845,14 @@ def update_training_job_labels_with_options( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.UpdateTrainingJobLabelsResponse: + """ + @summary 更新一个TrainingJob的Labels + + @param request: UpdateTrainingJobLabelsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: UpdateTrainingJobLabelsResponse + """ UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.labels): @@ -10168,6 +12884,14 @@ async def update_training_job_labels_with_options_async( headers: Dict[str, str], runtime: util_models.RuntimeOptions, ) -> pai_studio_20220112_models.UpdateTrainingJobLabelsResponse: + """ + @summary 更新一个TrainingJob的Labels + + @param request: UpdateTrainingJobLabelsRequest + @param headers: map + @param runtime: runtime options for this request RuntimeOptions + @return: UpdateTrainingJobLabelsResponse + """ UtilClient.validate_model(request) body = {} if not UtilClient.is_unset(request.labels): @@ -10197,6 +12921,12 @@ def update_training_job_labels( training_job_id: str, request: pai_studio_20220112_models.UpdateTrainingJobLabelsRequest, ) -> pai_studio_20220112_models.UpdateTrainingJobLabelsResponse: + """ + @summary 更新一个TrainingJob的Labels + + @param request: UpdateTrainingJobLabelsRequest + @return: UpdateTrainingJobLabelsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return self.update_training_job_labels_with_options(training_job_id, request, headers, runtime) @@ -10206,6 +12936,12 @@ async def update_training_job_labels_async( training_job_id: str, request: pai_studio_20220112_models.UpdateTrainingJobLabelsRequest, ) -> pai_studio_20220112_models.UpdateTrainingJobLabelsResponse: + """ + @summary 更新一个TrainingJob的Labels + + @param request: UpdateTrainingJobLabelsRequest + @return: UpdateTrainingJobLabelsResponse + """ runtime = util_models.RuntimeOptions() headers = {} return await self.update_training_job_labels_with_options_async(training_job_id, request, headers, runtime) diff --git a/pai/libs/alibabacloud_paistudio20220112/models.py b/pai/libs/alibabacloud_paistudio20220112/models.py index e5f6ecf..81eda37 100644 --- a/pai/libs/alibabacloud_paistudio20220112/models.py +++ b/pai/libs/alibabacloud_paistudio20220112/models.py @@ -43,7 +43,9 @@ def __init__( value: str = None, version: str = None, ): + # This parameter is required. self.value = value + # This parameter is required. self.version = version def validate(self): @@ -75,6 +77,7 @@ def __init__( self, policy: AlgorithmSpecComputeResourcePolicy = None, ): + # This parameter is required. self.policy = policy def validate(self): @@ -346,9 +349,11 @@ def __init__( self.default_value = default_value self.description = description self.display_name = display_name + # This parameter is required. self.name = name self.range = range self.required = required + # This parameter is required. self.type = type def validate(self): @@ -407,6 +412,7 @@ def __init__( supported_channel_types: List[str] = None, ): self.description = description + # This parameter is required. self.name = name self.properties = properties self.required = required @@ -456,7 +462,9 @@ def __init__( regex: str = None, ): self.description = description + # This parameter is required. self.name = name + # This parameter is required. self.regex = regex def validate(self): @@ -494,8 +502,11 @@ def __init__( operator: str = None, values: List[str] = None, ): + # This parameter is required. self.key = key + # This parameter is required. self.operator = operator + # This parameter is required. self.values = values def validate(self): @@ -545,12 +556,15 @@ def __init__( supports_distributed_training: bool = None, ): self.code_dir = code_dir + # This parameter is required. self.command = command self.compute_resource = compute_resource self.customization = customization self.hyper_parameters = hyper_parameters + # This parameter is required. self.image = image self.input_channels = input_channels + # This parameter is required. self.job_type = job_type self.metric_definitions = metric_definitions self.output_channels = output_channels @@ -761,7 +775,9 @@ def __init__( name: str = None, value: str = None, ): + # This parameter is required. self.name = name + # This parameter is required. self.value = value def validate(self): @@ -802,10 +818,13 @@ def __init__( resource_requirements: List[ConditionExpression] = None, ): self.code_dir = code_dir + # This parameter is required. self.command = command self.hyper_parameters = hyper_parameters + # This parameter is required. self.image = image self.input_channels = input_channels + # This parameter is required. self.job_type = job_type self.metric_definitions = metric_definitions self.output_channels = output_channels @@ -999,6 +1018,51 @@ def from_map(self, m: dict = None): return self +class GPUMetric(TeaModel): + def __init__( + self, + index: int = None, + model: str = None, + status: int = None, + usage_rate: float = None, + ): + self.index = index + self.model = model + self.status = status + self.usage_rate = usage_rate + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.index is not None: + result['Index'] = self.index + if self.model is not None: + result['Model'] = self.model + if self.status is not None: + result['Status'] = self.status + if self.usage_rate is not None: + result['UsageRate'] = self.usage_rate + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Index') is not None: + self.index = m.get('Index') + if m.get('Model') is not None: + self.model = m.get('Model') + if m.get('Status') is not None: + self.status = m.get('Status') + if m.get('UsageRate') is not None: + self.usage_rate = m.get('UsageRate') + return self + + class JobViewMetric(TeaModel): def __init__( self, @@ -1173,6 +1237,7 @@ def __init__( gmt_modified_time: str = None, gmt_started_time: str = None, machine_group_id: str = None, + order_instance_id: str = None, payment_duration: str = None, payment_duration_unit: str = None, payment_type: str = None, @@ -1191,6 +1256,7 @@ def __init__( self.gmt_modified_time = gmt_modified_time self.gmt_started_time = gmt_started_time self.machine_group_id = machine_group_id + self.order_instance_id = order_instance_id self.payment_duration = payment_duration self.payment_duration_unit = payment_duration_unit self.payment_type = payment_type @@ -1227,6 +1293,8 @@ def to_map(self): result['GmtStartedTime'] = self.gmt_started_time if self.machine_group_id is not None: result['MachineGroupID'] = self.machine_group_id + if self.order_instance_id is not None: + result['OrderInstanceId'] = self.order_instance_id if self.payment_duration is not None: result['PaymentDuration'] = self.payment_duration if self.payment_duration_unit is not None: @@ -1265,6 +1333,8 @@ def from_map(self, m: dict = None): self.gmt_started_time = m.get('GmtStartedTime') if m.get('MachineGroupID') is not None: self.machine_group_id = m.get('MachineGroupID') + if m.get('OrderInstanceId') is not None: + self.order_instance_id = m.get('OrderInstanceId') if m.get('PaymentDuration') is not None: self.payment_duration = m.get('PaymentDuration') if m.get('PaymentDurationUnit') is not None: @@ -1350,6 +1420,39 @@ def from_map(self, m: dict = None): return self +class UserInfo(TeaModel): + def __init__( + self, + user_id: str = None, + user_name: str = None, + ): + self.user_id = user_id + self.user_name = user_name + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.user_id is not None: + result['UserId'] = self.user_id + if self.user_name is not None: + result['UserName'] = self.user_name + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + if m.get('UserName') is not None: + self.user_name = m.get('UserName') + return self + + class Node(TeaModel): def __init__( self, @@ -1372,6 +1475,7 @@ def __init__( node_status: str = None, node_type: str = None, order_status: str = None, + pod_num: int = None, reason_code: str = None, reason_message: str = None, request_cpu: str = None, @@ -1379,6 +1483,8 @@ def __init__( request_memory: str = None, resource_group_id: str = None, resource_group_name: str = None, + users: List[UserInfo] = None, + workload_num: int = None, ): self.accelerator_type = accelerator_type self.bound_quotas = bound_quotas @@ -1399,6 +1505,7 @@ def __init__( self.node_status = node_status self.node_type = node_type self.order_status = order_status + self.pod_num = pod_num self.reason_code = reason_code self.reason_message = reason_message self.request_cpu = request_cpu @@ -1406,12 +1513,18 @@ def __init__( self.request_memory = request_memory self.resource_group_id = resource_group_id self.resource_group_name = resource_group_name + self.users = users + self.workload_num = workload_num def validate(self): if self.bound_quotas: for k in self.bound_quotas: if k: k.validate() + if self.users: + for k in self.users: + if k: + k.validate() def to_map(self): _map = super().to_map() @@ -1459,6 +1572,8 @@ def to_map(self): result['NodeType'] = self.node_type if self.order_status is not None: result['OrderStatus'] = self.order_status + if self.pod_num is not None: + result['PodNum'] = self.pod_num if self.reason_code is not None: result['ReasonCode'] = self.reason_code if self.reason_message is not None: @@ -1473,6 +1588,12 @@ def to_map(self): result['ResourceGroupId'] = self.resource_group_id if self.resource_group_name is not None: result['ResourceGroupName'] = self.resource_group_name + result['Users'] = [] + if self.users is not None: + for k in self.users: + result['Users'].append(k.to_map() if k else None) + if self.workload_num is not None: + result['WorkloadNum'] = self.workload_num return result def from_map(self, m: dict = None): @@ -1518,6 +1639,8 @@ def from_map(self, m: dict = None): self.node_type = m.get('NodeType') if m.get('OrderStatus') is not None: self.order_status = m.get('OrderStatus') + if m.get('PodNum') is not None: + self.pod_num = m.get('PodNum') if m.get('ReasonCode') is not None: self.reason_code = m.get('ReasonCode') if m.get('ReasonMessage') is not None: @@ -1532,6 +1655,96 @@ def from_map(self, m: dict = None): self.resource_group_id = m.get('ResourceGroupId') if m.get('ResourceGroupName') is not None: self.resource_group_name = m.get('ResourceGroupName') + self.users = [] + if m.get('Users') is not None: + for k in m.get('Users'): + temp_model = UserInfo() + self.users.append(temp_model.from_map(k)) + if m.get('WorkloadNum') is not None: + self.workload_num = m.get('WorkloadNum') + return self + + +class NodeGPUMetric(TeaModel): + def __init__( + self, + accelerator_type: str = None, + gpucount: int = None, + gpumetrics: List[GPUMetric] = None, + gputype: str = None, + memory_util: float = None, + node_id: str = None, + node_type: str = None, + total_memory: float = None, + used_memory: float = None, + ): + self.accelerator_type = accelerator_type + self.gpucount = gpucount + self.gpumetrics = gpumetrics + self.gputype = gputype + self.memory_util = memory_util + self.node_id = node_id + self.node_type = node_type + self.total_memory = total_memory + self.used_memory = used_memory + + def validate(self): + if self.gpumetrics: + for k in self.gpumetrics: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.accelerator_type is not None: + result['AcceleratorType'] = self.accelerator_type + if self.gpucount is not None: + result['GPUCount'] = self.gpucount + result['GPUMetrics'] = [] + if self.gpumetrics is not None: + for k in self.gpumetrics: + result['GPUMetrics'].append(k.to_map() if k else None) + if self.gputype is not None: + result['GPUType'] = self.gputype + if self.memory_util is not None: + result['MemoryUtil'] = self.memory_util + if self.node_id is not None: + result['NodeId'] = self.node_id + if self.node_type is not None: + result['NodeType'] = self.node_type + if self.total_memory is not None: + result['TotalMemory'] = self.total_memory + if self.used_memory is not None: + result['UsedMemory'] = self.used_memory + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('AcceleratorType') is not None: + self.accelerator_type = m.get('AcceleratorType') + if m.get('GPUCount') is not None: + self.gpucount = m.get('GPUCount') + self.gpumetrics = [] + if m.get('GPUMetrics') is not None: + for k in m.get('GPUMetrics'): + temp_model = GPUMetric() + self.gpumetrics.append(temp_model.from_map(k)) + if m.get('GPUType') is not None: + self.gputype = m.get('GPUType') + if m.get('MemoryUtil') is not None: + self.memory_util = m.get('MemoryUtil') + if m.get('NodeId') is not None: + self.node_id = m.get('NodeId') + if m.get('NodeType') is not None: + self.node_type = m.get('NodeType') + if m.get('TotalMemory') is not None: + self.total_memory = m.get('TotalMemory') + if m.get('UsedMemory') is not None: + self.used_memory = m.get('UsedMemory') return self @@ -1979,6 +2192,7 @@ def __init__( self, code: str = None, code_type: str = None, + gmt_created_time: str = None, gmt_dequeued_time: str = None, gmt_enqueued_time: str = None, gmt_position_modified_time: str = None, @@ -1992,12 +2206,15 @@ def __init__( status: str = None, sub_status: str = None, user_id: str = None, + user_name: str = None, workload_id: str = None, + workload_name: str = None, workload_type: str = None, workspace_id: str = None, ): self.code = code self.code_type = code_type + self.gmt_created_time = gmt_created_time self.gmt_dequeued_time = gmt_dequeued_time self.gmt_enqueued_time = gmt_enqueued_time self.gmt_position_modified_time = gmt_position_modified_time @@ -2011,7 +2228,9 @@ def __init__( self.status = status self.sub_status = sub_status self.user_id = user_id + self.user_name = user_name self.workload_id = workload_id + self.workload_name = workload_name self.workload_type = workload_type self.workspace_id = workspace_id @@ -2029,6 +2248,8 @@ def to_map(self): result['Code'] = self.code if self.code_type is not None: result['CodeType'] = self.code_type + if self.gmt_created_time is not None: + result['GmtCreatedTime'] = self.gmt_created_time if self.gmt_dequeued_time is not None: result['GmtDequeuedTime'] = self.gmt_dequeued_time if self.gmt_enqueued_time is not None: @@ -2055,8 +2276,12 @@ def to_map(self): result['SubStatus'] = self.sub_status if self.user_id is not None: result['UserId'] = self.user_id + if self.user_name is not None: + result['UserName'] = self.user_name if self.workload_id is not None: result['WorkloadId'] = self.workload_id + if self.workload_name is not None: + result['WorkloadName'] = self.workload_name if self.workload_type is not None: result['WorkloadType'] = self.workload_type if self.workspace_id is not None: @@ -2069,6 +2294,8 @@ def from_map(self, m: dict = None): self.code = m.get('Code') if m.get('CodeType') is not None: self.code_type = m.get('CodeType') + if m.get('GmtCreatedTime') is not None: + self.gmt_created_time = m.get('GmtCreatedTime') if m.get('GmtDequeuedTime') is not None: self.gmt_dequeued_time = m.get('GmtDequeuedTime') if m.get('GmtEnqueuedTime') is not None: @@ -2096,8 +2323,12 @@ def from_map(self, m: dict = None): self.sub_status = m.get('SubStatus') if m.get('UserId') is not None: self.user_id = m.get('UserId') + if m.get('UserName') is not None: + self.user_name = m.get('UserName') if m.get('WorkloadId') is not None: self.workload_id = m.get('WorkloadId') + if m.get('WorkloadName') is not None: + self.workload_name = m.get('WorkloadName') if m.get('WorkloadType') is not None: self.workload_type = m.get('WorkloadType') if m.get('WorkspaceId') is not None: @@ -2315,6 +2546,7 @@ def __init__( acs: ACS = None, cluster_id: str = None, default_gpudriver: str = None, + enable_preempt_subquota_workloads: bool = None, resource_specs: List[WorkspaceSpecs] = None, support_gpudrivers: List[str] = None, support_rdma: bool = None, @@ -2323,6 +2555,7 @@ def __init__( self.acs = acs self.cluster_id = cluster_id self.default_gpudriver = default_gpudriver + self.enable_preempt_subquota_workloads = enable_preempt_subquota_workloads self.resource_specs = resource_specs self.support_gpudrivers = support_gpudrivers self.support_rdma = support_rdma @@ -2350,6 +2583,8 @@ def to_map(self): result['ClusterId'] = self.cluster_id if self.default_gpudriver is not None: result['DefaultGPUDriver'] = self.default_gpudriver + if self.enable_preempt_subquota_workloads is not None: + result['EnablePreemptSubquotaWorkloads'] = self.enable_preempt_subquota_workloads result['ResourceSpecs'] = [] if self.resource_specs is not None: for k in self.resource_specs: @@ -2371,6 +2606,8 @@ def from_map(self, m: dict = None): self.cluster_id = m.get('ClusterId') if m.get('DefaultGPUDriver') is not None: self.default_gpudriver = m.get('DefaultGPUDriver') + if m.get('EnablePreemptSubquotaWorkloads') is not None: + self.enable_preempt_subquota_workloads = m.get('EnablePreemptSubquotaWorkloads') self.resource_specs = [] if m.get('ResourceSpecs') is not None: for k in m.get('ResourceSpecs'): @@ -2651,6 +2888,45 @@ def from_map(self, m: dict = None): return self +class QuotaJob(TeaModel): + def __init__( + self, + queuing: int = None, + running: int = None, + total: int = None, + ): + self.queuing = queuing + self.running = running + self.total = total + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.queuing is not None: + result['Queuing'] = self.queuing + if self.running is not None: + result['Running'] = self.running + if self.total is not None: + result['Total'] = self.total + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Queuing') is not None: + self.queuing = m.get('Queuing') + if m.get('Running') is not None: + self.running = m.get('Running') + if m.get('Total') is not None: + self.total = m.get('Total') + return self + + class QuotaJobViewMetric(TeaModel): def __init__( self, @@ -2968,57 +3244,28 @@ def from_map(self, m: dict = None): return self -class QuotaUserViewMetric(TeaModel): +class WorkloadDetails(TeaModel): def __init__( self, - cpunode_number: int = None, - cpuusage_rate: str = None, - cpu_job_names: List[str] = None, - cpu_node_names: List[str] = None, - disk_read_rate: str = None, - disk_write_rate: str = None, - gpunode_number: int = None, - gpuusage_rate: str = None, - gpu_job_names: List[str] = None, - gpu_node_names: List[str] = None, - job_type: str = None, - memory_usage_rate: str = None, - network_input_rate: str = None, - network_output_rate: str = None, - node_names: List[str] = None, - request_cpu: int = None, - request_gpu: int = None, - request_memory: int = None, - total_cpu: int = None, - total_gpu: int = None, - total_memory: int = None, - user_id: str = None, + dlc: QuotaJob = None, + dsw: QuotaJob = None, + eas: QuotaJob = None, + summary: QuotaJob = None, ): - self.cpunode_number = cpunode_number - self.cpuusage_rate = cpuusage_rate - self.cpu_job_names = cpu_job_names - self.cpu_node_names = cpu_node_names - self.disk_read_rate = disk_read_rate - self.disk_write_rate = disk_write_rate - self.gpunode_number = gpunode_number - self.gpuusage_rate = gpuusage_rate - self.gpu_job_names = gpu_job_names - self.gpu_node_names = gpu_node_names - self.job_type = job_type - self.memory_usage_rate = memory_usage_rate - self.network_input_rate = network_input_rate - self.network_output_rate = network_output_rate - self.node_names = node_names - self.request_cpu = request_cpu - self.request_gpu = request_gpu - self.request_memory = request_memory - self.total_cpu = total_cpu - self.total_gpu = total_gpu - self.total_memory = total_memory - self.user_id = user_id + self.dlc = dlc + self.dsw = dsw + self.eas = eas + self.summary = summary def validate(self): - pass + if self.dlc: + self.dlc.validate() + if self.dsw: + self.dsw.validate() + if self.eas: + self.eas.validate() + if self.summary: + self.summary.validate() def to_map(self): _map = super().to_map() @@ -3026,80 +3273,318 @@ def to_map(self): return _map result = dict() - if self.cpunode_number is not None: - result['CPUNodeNumber'] = self.cpunode_number - if self.cpuusage_rate is not None: - result['CPUUsageRate'] = self.cpuusage_rate - if self.cpu_job_names is not None: - result['CpuJobNames'] = self.cpu_job_names - if self.cpu_node_names is not None: - result['CpuNodeNames'] = self.cpu_node_names - if self.disk_read_rate is not None: - result['DiskReadRate'] = self.disk_read_rate - if self.disk_write_rate is not None: - result['DiskWriteRate'] = self.disk_write_rate - if self.gpunode_number is not None: - result['GPUNodeNumber'] = self.gpunode_number - if self.gpuusage_rate is not None: - result['GPUUsageRate'] = self.gpuusage_rate - if self.gpu_job_names is not None: - result['GpuJobNames'] = self.gpu_job_names - if self.gpu_node_names is not None: - result['GpuNodeNames'] = self.gpu_node_names - if self.job_type is not None: - result['JobType'] = self.job_type - if self.memory_usage_rate is not None: - result['MemoryUsageRate'] = self.memory_usage_rate - if self.network_input_rate is not None: - result['NetworkInputRate'] = self.network_input_rate - if self.network_output_rate is not None: - result['NetworkOutputRate'] = self.network_output_rate - if self.node_names is not None: - result['NodeNames'] = self.node_names - if self.request_cpu is not None: - result['RequestCPU'] = self.request_cpu - if self.request_gpu is not None: - result['RequestGPU'] = self.request_gpu - if self.request_memory is not None: - result['RequestMemory'] = self.request_memory - if self.total_cpu is not None: - result['TotalCPU'] = self.total_cpu - if self.total_gpu is not None: - result['TotalGPU'] = self.total_gpu - if self.total_memory is not None: - result['TotalMemory'] = self.total_memory - if self.user_id is not None: - result['UserId'] = self.user_id + if self.dlc is not None: + result['DLC'] = self.dlc.to_map() + if self.dsw is not None: + result['DSW'] = self.dsw.to_map() + if self.eas is not None: + result['EAS'] = self.eas.to_map() + if self.summary is not None: + result['Summary'] = self.summary.to_map() return result def from_map(self, m: dict = None): m = m or dict() - if m.get('CPUNodeNumber') is not None: - self.cpunode_number = m.get('CPUNodeNumber') - if m.get('CPUUsageRate') is not None: - self.cpuusage_rate = m.get('CPUUsageRate') - if m.get('CpuJobNames') is not None: - self.cpu_job_names = m.get('CpuJobNames') - if m.get('CpuNodeNames') is not None: - self.cpu_node_names = m.get('CpuNodeNames') - if m.get('DiskReadRate') is not None: - self.disk_read_rate = m.get('DiskReadRate') - if m.get('DiskWriteRate') is not None: - self.disk_write_rate = m.get('DiskWriteRate') - if m.get('GPUNodeNumber') is not None: - self.gpunode_number = m.get('GPUNodeNumber') - if m.get('GPUUsageRate') is not None: - self.gpuusage_rate = m.get('GPUUsageRate') - if m.get('GpuJobNames') is not None: - self.gpu_job_names = m.get('GpuJobNames') - if m.get('GpuNodeNames') is not None: - self.gpu_node_names = m.get('GpuNodeNames') - if m.get('JobType') is not None: - self.job_type = m.get('JobType') - if m.get('MemoryUsageRate') is not None: - self.memory_usage_rate = m.get('MemoryUsageRate') - if m.get('NetworkInputRate') is not None: - self.network_input_rate = m.get('NetworkInputRate') + if m.get('DLC') is not None: + temp_model = QuotaJob() + self.dlc = temp_model.from_map(m['DLC']) + if m.get('DSW') is not None: + temp_model = QuotaJob() + self.dsw = temp_model.from_map(m['DSW']) + if m.get('EAS') is not None: + temp_model = QuotaJob() + self.eas = temp_model.from_map(m['EAS']) + if m.get('Summary') is not None: + temp_model = QuotaJob() + self.summary = temp_model.from_map(m['Summary']) + return self + + +class QuotaTopo(TeaModel): + def __init__( + self, + depth: str = None, + parent_quota_id: str = None, + quota_details: QuotaDetails = None, + quota_id: str = None, + quota_name: str = None, + resource_type: str = None, + workload_details: WorkloadDetails = None, + ): + self.depth = depth + self.parent_quota_id = parent_quota_id + self.quota_details = quota_details + self.quota_id = quota_id + self.quota_name = quota_name + self.resource_type = resource_type + self.workload_details = workload_details + + def validate(self): + if self.quota_details: + self.quota_details.validate() + if self.workload_details: + self.workload_details.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.depth is not None: + result['Depth'] = self.depth + if self.parent_quota_id is not None: + result['ParentQuotaId'] = self.parent_quota_id + if self.quota_details is not None: + result['QuotaDetails'] = self.quota_details.to_map() + if self.quota_id is not None: + result['QuotaId'] = self.quota_id + if self.quota_name is not None: + result['QuotaName'] = self.quota_name + if self.resource_type is not None: + result['ResourceType'] = self.resource_type + if self.workload_details is not None: + result['WorkloadDetails'] = self.workload_details.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Depth') is not None: + self.depth = m.get('Depth') + if m.get('ParentQuotaId') is not None: + self.parent_quota_id = m.get('ParentQuotaId') + if m.get('QuotaDetails') is not None: + temp_model = QuotaDetails() + self.quota_details = temp_model.from_map(m['QuotaDetails']) + if m.get('QuotaId') is not None: + self.quota_id = m.get('QuotaId') + if m.get('QuotaName') is not None: + self.quota_name = m.get('QuotaName') + if m.get('ResourceType') is not None: + self.resource_type = m.get('ResourceType') + if m.get('WorkloadDetails') is not None: + temp_model = WorkloadDetails() + self.workload_details = temp_model.from_map(m['WorkloadDetails']) + return self + + +class QuotaUserResources(TeaModel): + def __init__( + self, + submitted: ResourceAmount = None, + used: ResourceAmount = None, + ): + self.submitted = submitted + self.used = used + + def validate(self): + if self.submitted: + self.submitted.validate() + if self.used: + self.used.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.submitted is not None: + result['Submitted'] = self.submitted.to_map() + if self.used is not None: + result['Used'] = self.used.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Submitted') is not None: + temp_model = ResourceAmount() + self.submitted = temp_model.from_map(m['Submitted']) + if m.get('Used') is not None: + temp_model = ResourceAmount() + self.used = temp_model.from_map(m['Used']) + return self + + +class QuotaUser(TeaModel): + def __init__( + self, + resources: QuotaUserResources = None, + user_id: str = None, + username: str = None, + workload_count: int = None, + ): + self.resources = resources + self.user_id = user_id + self.username = username + self.workload_count = workload_count + + def validate(self): + if self.resources: + self.resources.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.resources is not None: + result['Resources'] = self.resources.to_map() + if self.user_id is not None: + result['UserId'] = self.user_id + if self.username is not None: + result['Username'] = self.username + if self.workload_count is not None: + result['WorkloadCount'] = self.workload_count + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Resources') is not None: + temp_model = QuotaUserResources() + self.resources = temp_model.from_map(m['Resources']) + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + if m.get('Username') is not None: + self.username = m.get('Username') + if m.get('WorkloadCount') is not None: + self.workload_count = m.get('WorkloadCount') + return self + + +class QuotaUserViewMetric(TeaModel): + def __init__( + self, + cpunode_number: int = None, + cpuusage_rate: str = None, + cpu_job_names: List[str] = None, + cpu_node_names: List[str] = None, + disk_read_rate: str = None, + disk_write_rate: str = None, + gpunode_number: int = None, + gpuusage_rate: str = None, + gpu_job_names: List[str] = None, + gpu_node_names: List[str] = None, + job_type: str = None, + memory_usage_rate: str = None, + network_input_rate: str = None, + network_output_rate: str = None, + node_names: List[str] = None, + request_cpu: int = None, + request_gpu: int = None, + request_memory: int = None, + total_cpu: int = None, + total_gpu: int = None, + total_memory: int = None, + user_id: str = None, + ): + self.cpunode_number = cpunode_number + self.cpuusage_rate = cpuusage_rate + self.cpu_job_names = cpu_job_names + self.cpu_node_names = cpu_node_names + self.disk_read_rate = disk_read_rate + self.disk_write_rate = disk_write_rate + self.gpunode_number = gpunode_number + self.gpuusage_rate = gpuusage_rate + self.gpu_job_names = gpu_job_names + self.gpu_node_names = gpu_node_names + self.job_type = job_type + self.memory_usage_rate = memory_usage_rate + self.network_input_rate = network_input_rate + self.network_output_rate = network_output_rate + self.node_names = node_names + self.request_cpu = request_cpu + self.request_gpu = request_gpu + self.request_memory = request_memory + self.total_cpu = total_cpu + self.total_gpu = total_gpu + self.total_memory = total_memory + self.user_id = user_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.cpunode_number is not None: + result['CPUNodeNumber'] = self.cpunode_number + if self.cpuusage_rate is not None: + result['CPUUsageRate'] = self.cpuusage_rate + if self.cpu_job_names is not None: + result['CpuJobNames'] = self.cpu_job_names + if self.cpu_node_names is not None: + result['CpuNodeNames'] = self.cpu_node_names + if self.disk_read_rate is not None: + result['DiskReadRate'] = self.disk_read_rate + if self.disk_write_rate is not None: + result['DiskWriteRate'] = self.disk_write_rate + if self.gpunode_number is not None: + result['GPUNodeNumber'] = self.gpunode_number + if self.gpuusage_rate is not None: + result['GPUUsageRate'] = self.gpuusage_rate + if self.gpu_job_names is not None: + result['GpuJobNames'] = self.gpu_job_names + if self.gpu_node_names is not None: + result['GpuNodeNames'] = self.gpu_node_names + if self.job_type is not None: + result['JobType'] = self.job_type + if self.memory_usage_rate is not None: + result['MemoryUsageRate'] = self.memory_usage_rate + if self.network_input_rate is not None: + result['NetworkInputRate'] = self.network_input_rate + if self.network_output_rate is not None: + result['NetworkOutputRate'] = self.network_output_rate + if self.node_names is not None: + result['NodeNames'] = self.node_names + if self.request_cpu is not None: + result['RequestCPU'] = self.request_cpu + if self.request_gpu is not None: + result['RequestGPU'] = self.request_gpu + if self.request_memory is not None: + result['RequestMemory'] = self.request_memory + if self.total_cpu is not None: + result['TotalCPU'] = self.total_cpu + if self.total_gpu is not None: + result['TotalGPU'] = self.total_gpu + if self.total_memory is not None: + result['TotalMemory'] = self.total_memory + if self.user_id is not None: + result['UserId'] = self.user_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('CPUNodeNumber') is not None: + self.cpunode_number = m.get('CPUNodeNumber') + if m.get('CPUUsageRate') is not None: + self.cpuusage_rate = m.get('CPUUsageRate') + if m.get('CpuJobNames') is not None: + self.cpu_job_names = m.get('CpuJobNames') + if m.get('CpuNodeNames') is not None: + self.cpu_node_names = m.get('CpuNodeNames') + if m.get('DiskReadRate') is not None: + self.disk_read_rate = m.get('DiskReadRate') + if m.get('DiskWriteRate') is not None: + self.disk_write_rate = m.get('DiskWriteRate') + if m.get('GPUNodeNumber') is not None: + self.gpunode_number = m.get('GPUNodeNumber') + if m.get('GPUUsageRate') is not None: + self.gpuusage_rate = m.get('GPUUsageRate') + if m.get('GpuJobNames') is not None: + self.gpu_job_names = m.get('GpuJobNames') + if m.get('GpuNodeNames') is not None: + self.gpu_node_names = m.get('GpuNodeNames') + if m.get('JobType') is not None: + self.job_type = m.get('JobType') + if m.get('MemoryUsageRate') is not None: + self.memory_usage_rate = m.get('MemoryUsageRate') + if m.get('NetworkInputRate') is not None: + self.network_input_rate = m.get('NetworkInputRate') if m.get('NetworkOutputRate') is not None: self.network_output_rate = m.get('NetworkOutputRate') if m.get('NodeNames') is not None: @@ -3428,29 +3913,62 @@ def from_map(self, m: dict = None): return self -class UserViewMetric(TeaModel): +class TimeRangeFilter(TeaModel): def __init__( self, - cpunode_number: int = None, - cpuusage_rate: str = None, - cpu_job_names: List[str] = None, - cpu_node_names: List[str] = None, - disk_read_rate: str = None, - disk_write_rate: str = None, - gpunode_number: int = None, - gpuusage_rate: str = None, - gpu_job_names: List[str] = None, - gpu_node_names: List[str] = None, - job_type: str = None, - memory_usage_rate: str = None, - network_input_rate: str = None, - network_output_rate: str = None, - node_names: List[str] = None, - request_cpu: int = None, - request_gpu: int = None, - request_memory: int = None, - resource_group_id: str = None, - total_cpu: int = None, + end_time: str = None, + start_time: str = None, + ): + self.end_time = end_time + self.start_time = start_time + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.end_time is not None: + result['EndTime'] = self.end_time + if self.start_time is not None: + result['StartTime'] = self.start_time + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('EndTime') is not None: + self.end_time = m.get('EndTime') + if m.get('StartTime') is not None: + self.start_time = m.get('StartTime') + return self + + +class UserViewMetric(TeaModel): + def __init__( + self, + cpunode_number: int = None, + cpuusage_rate: str = None, + cpu_job_names: List[str] = None, + cpu_node_names: List[str] = None, + disk_read_rate: str = None, + disk_write_rate: str = None, + gpunode_number: int = None, + gpuusage_rate: str = None, + gpu_job_names: List[str] = None, + gpu_node_names: List[str] = None, + job_type: str = None, + memory_usage_rate: str = None, + network_input_rate: str = None, + network_output_rate: str = None, + node_names: List[str] = None, + request_cpu: int = None, + request_gpu: int = None, + request_memory: int = None, + resource_group_id: str = None, + total_cpu: int = None, total_gpu: int = None, total_memory: int = None, user_id: str = None, @@ -3587,16 +4105,12 @@ def from_map(self, m: dict = None): return self -class BuildLLMSnapshotRequestWorkloadContainer(TeaModel): +class CheckInstanceWebTerminalRequest(TeaModel): def __init__( self, - image: str = None, - port: int = None, - user_command: str = None, + check_info: str = None, ): - self.image = image - self.port = port - self.user_command = user_command + self.check_info = check_info def validate(self): pass @@ -3607,35 +4121,23 @@ def to_map(self): return _map result = dict() - if self.image is not None: - result['Image'] = self.image - if self.port is not None: - result['Port'] = self.port - if self.user_command is not None: - result['UserCommand'] = self.user_command + if self.check_info is not None: + result['CheckInfo'] = self.check_info return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Image') is not None: - self.image = m.get('Image') - if m.get('Port') is not None: - self.port = m.get('Port') - if m.get('UserCommand') is not None: - self.user_command = m.get('UserCommand') + if m.get('CheckInfo') is not None: + self.check_info = m.get('CheckInfo') return self -class BuildLLMSnapshotRequestWorkloadExtraConfig(TeaModel): +class CheckInstanceWebTerminalResponseBody(TeaModel): def __init__( self, - enable_webservice: bool = None, - job_max_running_time_minutes: int = None, - third_party_lib_dir: str = None, + request_id: str = None, ): - self.enable_webservice = enable_webservice - self.job_max_running_time_minutes = job_max_running_time_minutes - self.third_party_lib_dir = third_party_lib_dir + self.request_id = request_id def validate(self): pass @@ -3646,40 +4148,31 @@ def to_map(self): return _map result = dict() - if self.enable_webservice is not None: - result['EnableWebservice'] = self.enable_webservice - if self.job_max_running_time_minutes is not None: - result['JobMaxRunningTimeMinutes'] = self.job_max_running_time_minutes - if self.third_party_lib_dir is not None: - result['ThirdPartyLibDir'] = self.third_party_lib_dir + if self.request_id is not None: + result['RequestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('EnableWebservice') is not None: - self.enable_webservice = m.get('EnableWebservice') - if m.get('JobMaxRunningTimeMinutes') is not None: - self.job_max_running_time_minutes = m.get('JobMaxRunningTimeMinutes') - if m.get('ThirdPartyLibDir') is not None: - self.third_party_lib_dir = m.get('ThirdPartyLibDir') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') return self -class BuildLLMSnapshotRequestWorkloadResourceSpecResourceConfig(TeaModel): +class CheckInstanceWebTerminalResponse(TeaModel): def __init__( self, - cpu: int = None, - gpu: int = None, - memory_in_gi_b: int = None, - resource_group: str = None, + headers: Dict[str, str] = None, + status_code: int = None, + body: CheckInstanceWebTerminalResponseBody = None, ): - self.cpu = cpu - self.gpu = gpu - self.memory_in_gi_b = memory_in_gi_b - self.resource_group = resource_group + self.headers = headers + self.status_code = status_code + self.body = body def validate(self): - pass + if self.body: + self.body.validate() def to_map(self): _map = super().to_map() @@ -3687,43 +4180,41 @@ def to_map(self): return _map result = dict() - if self.cpu is not None: - result['Cpu'] = self.cpu - if self.gpu is not None: - result['Gpu'] = self.gpu - if self.memory_in_gi_b is not None: - result['MemoryInGiB'] = self.memory_in_gi_b - if self.resource_group is not None: - result['ResourceGroup'] = self.resource_group + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Cpu') is not None: - self.cpu = m.get('Cpu') - if m.get('Gpu') is not None: - self.gpu = m.get('Gpu') - if m.get('MemoryInGiB') is not None: - self.memory_in_gi_b = m.get('MemoryInGiB') - if m.get('ResourceGroup') is not None: - self.resource_group = m.get('ResourceGroup') + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = CheckInstanceWebTerminalResponseBody() + self.body = temp_model.from_map(m['body']) return self -class BuildLLMSnapshotRequestWorkloadResourceSpec(TeaModel): +class CreateAI4DDefaultBucketResponseBody(TeaModel): def __init__( self, - ecs_spec: str = None, - instance_num: int = None, - resource_config: BuildLLMSnapshotRequestWorkloadResourceSpecResourceConfig = None, + extranet_endpoint: str = None, + intranet_endpoint: str = None, + name: str = None, + request_id: str = None, ): - self.ecs_spec = ecs_spec - self.instance_num = instance_num - self.resource_config = resource_config + self.extranet_endpoint = extranet_endpoint + self.intranet_endpoint = intranet_endpoint + self.name = name + self.request_id = request_id def validate(self): - if self.resource_config: - self.resource_config.validate() + pass def to_map(self): _map = super().to_map() @@ -3731,43 +4222,43 @@ def to_map(self): return _map result = dict() - if self.ecs_spec is not None: - result['EcsSpec'] = self.ecs_spec - if self.instance_num is not None: - result['InstanceNum'] = self.instance_num - if self.resource_config is not None: - result['ResourceConfig'] = self.resource_config.to_map() + if self.extranet_endpoint is not None: + result['ExtranetEndpoint'] = self.extranet_endpoint + if self.intranet_endpoint is not None: + result['IntranetEndpoint'] = self.intranet_endpoint + if self.name is not None: + result['Name'] = self.name + if self.request_id is not None: + result['RequestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('EcsSpec') is not None: - self.ecs_spec = m.get('EcsSpec') - if m.get('InstanceNum') is not None: - self.instance_num = m.get('InstanceNum') - if m.get('ResourceConfig') is not None: - temp_model = BuildLLMSnapshotRequestWorkloadResourceSpecResourceConfig() - self.resource_config = temp_model.from_map(m['ResourceConfig']) + if m.get('ExtranetEndpoint') is not None: + self.extranet_endpoint = m.get('ExtranetEndpoint') + if m.get('IntranetEndpoint') is not None: + self.intranet_endpoint = m.get('IntranetEndpoint') + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') return self -class BuildLLMSnapshotRequestWorkloadUserVpc(TeaModel): +class CreateAI4DDefaultBucketResponse(TeaModel): def __init__( self, - default_route: str = None, - extended_cidrs: List[str] = None, - security_group_id: str = None, - switch_id: str = None, - vpc_id: str = None, + headers: Dict[str, str] = None, + status_code: int = None, + body: CreateAI4DDefaultBucketResponseBody = None, ): - self.default_route = default_route - self.extended_cidrs = extended_cidrs - self.security_group_id = security_group_id - self.switch_id = switch_id - self.vpc_id = vpc_id + self.headers = headers + self.status_code = status_code + self.body = body def validate(self): - pass + if self.body: + self.body.validate() def to_map(self): _map = super().to_map() @@ -3775,55 +4266,41 @@ def to_map(self): return _map result = dict() - if self.default_route is not None: - result['DefaultRoute'] = self.default_route - if self.extended_cidrs is not None: - result['ExtendedCIDRs'] = self.extended_cidrs - if self.security_group_id is not None: - result['SecurityGroupId'] = self.security_group_id - if self.switch_id is not None: - result['SwitchId'] = self.switch_id - if self.vpc_id is not None: - result['VpcId'] = self.vpc_id + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() return result def from_map(self, m: dict = None): m = m or dict() - if m.get('DefaultRoute') is not None: - self.default_route = m.get('DefaultRoute') - if m.get('ExtendedCIDRs') is not None: - self.extended_cidrs = m.get('ExtendedCIDRs') - if m.get('SecurityGroupId') is not None: - self.security_group_id = m.get('SecurityGroupId') - if m.get('SwitchId') is not None: - self.switch_id = m.get('SwitchId') - if m.get('VpcId') is not None: - self.vpc_id = m.get('VpcId') + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = CreateAI4DDefaultBucketResponseBody() + self.body = temp_model.from_map(m['body']) return self -class BuildLLMSnapshotRequestWorkload(TeaModel): +class CreateAI4DSerivceRequest(TeaModel): def __init__( self, - container: BuildLLMSnapshotRequestWorkloadContainer = None, - extra_config: BuildLLMSnapshotRequestWorkloadExtraConfig = None, - resource_spec: BuildLLMSnapshotRequestWorkloadResourceSpec = None, - user_vpc: BuildLLMSnapshotRequestWorkloadUserVpc = None, + inference_spec: Dict[str, Any] = None, + service_type: str = None, + workspace_id: str = None, ): - self.container = container - self.extra_config = extra_config - self.resource_spec = resource_spec - self.user_vpc = user_vpc + self.inference_spec = inference_spec + # This parameter is required. + self.service_type = service_type + # This parameter is required. + self.workspace_id = workspace_id def validate(self): - if self.container: - self.container.validate() - if self.extra_config: - self.extra_config.validate() - if self.resource_spec: - self.resource_spec.validate() - if self.user_vpc: - self.user_vpc.validate() + pass def to_map(self): _map = super().to_map() @@ -3831,49 +4308,36 @@ def to_map(self): return _map result = dict() - if self.container is not None: - result['Container'] = self.container.to_map() - if self.extra_config is not None: - result['ExtraConfig'] = self.extra_config.to_map() - if self.resource_spec is not None: - result['ResourceSpec'] = self.resource_spec.to_map() - if self.user_vpc is not None: - result['UserVpc'] = self.user_vpc.to_map() + if self.inference_spec is not None: + result['InferenceSpec'] = self.inference_spec + if self.service_type is not None: + result['ServiceType'] = self.service_type + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Container') is not None: - temp_model = BuildLLMSnapshotRequestWorkloadContainer() - self.container = temp_model.from_map(m['Container']) - if m.get('ExtraConfig') is not None: - temp_model = BuildLLMSnapshotRequestWorkloadExtraConfig() - self.extra_config = temp_model.from_map(m['ExtraConfig']) - if m.get('ResourceSpec') is not None: - temp_model = BuildLLMSnapshotRequestWorkloadResourceSpec() - self.resource_spec = temp_model.from_map(m['ResourceSpec']) - if m.get('UserVpc') is not None: - temp_model = BuildLLMSnapshotRequestWorkloadUserVpc() - self.user_vpc = temp_model.from_map(m['UserVpc']) + if m.get('InferenceSpec') is not None: + self.inference_spec = m.get('InferenceSpec') + if m.get('ServiceType') is not None: + self.service_type = m.get('ServiceType') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class BuildLLMSnapshotRequest(TeaModel): +class CreateAI4DSerivceResponseBody(TeaModel): def __init__( self, - description: str = None, - display_name: str = None, - labels: Dict[str, Any] = None, - workload: BuildLLMSnapshotRequestWorkload = None, + request_id: str = None, + service_name: str = None, ): - self.description = description - self.display_name = display_name - self.labels = labels - self.workload = workload + self.request_id = request_id + self.service_name = service_name def validate(self): - if self.workload: - self.workload.validate() + pass def to_map(self): _map = super().to_map() @@ -3881,87 +4345,27 @@ def to_map(self): return _map result = dict() - if self.description is not None: - result['Description'] = self.description - if self.display_name is not None: - result['DisplayName'] = self.display_name - if self.labels is not None: - result['Labels'] = self.labels - if self.workload is not None: - result['Workload'] = self.workload.to_map() + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.service_name is not None: + result['ServiceName'] = self.service_name return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Description') is not None: - self.description = m.get('Description') - if m.get('DisplayName') is not None: - self.display_name = m.get('DisplayName') - if m.get('Labels') is not None: - self.labels = m.get('Labels') - if m.get('Workload') is not None: - temp_model = BuildLLMSnapshotRequestWorkload() - self.workload = temp_model.from_map(m['Workload']) - return self - - -class BuildLLMSnapshotResponseBody(TeaModel): - def __init__( - self, - job_id: str = None, - job_name: str = None, - job_request_id: str = None, - request_id: str = None, - status: str = None, - ): - self.job_id = job_id - self.job_name = job_name - self.job_request_id = job_request_id - self.request_id = request_id - self.status = status - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.job_id is not None: - result['JobId'] = self.job_id - if self.job_name is not None: - result['JobName'] = self.job_name - if self.job_request_id is not None: - result['JobRequestId'] = self.job_request_id - if self.request_id is not None: - result['RequestId'] = self.request_id - if self.status is not None: - result['Status'] = self.status - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('JobId') is not None: - self.job_id = m.get('JobId') - if m.get('JobName') is not None: - self.job_name = m.get('JobName') - if m.get('JobRequestId') is not None: - self.job_request_id = m.get('JobRequestId') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('Status') is not None: - self.status = m.get('Status') + if m.get('ServiceName') is not None: + self.service_name = m.get('ServiceName') return self -class BuildLLMSnapshotResponse(TeaModel): +class CreateAI4DSerivceResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: BuildLLMSnapshotResponseBody = None, + body: CreateAI4DSerivceResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -3992,17 +4396,23 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = BuildLLMSnapshotResponseBody() + temp_model = CreateAI4DSerivceResponseBody() self.body = temp_model.from_map(m['body']) return self -class CheckInstanceWebTerminalRequest(TeaModel): +class CreateAlgorithmRequest(TeaModel): def __init__( self, - check_info: str = None, + algorithm_description: str = None, + algorithm_name: str = None, + display_name: str = None, + workspace_id: str = None, ): - self.check_info = check_info + self.algorithm_description = algorithm_description + self.algorithm_name = algorithm_name + self.display_name = display_name + self.workspace_id = workspace_id def validate(self): pass @@ -4013,22 +4423,36 @@ def to_map(self): return _map result = dict() - if self.check_info is not None: - result['CheckInfo'] = self.check_info + if self.algorithm_description is not None: + result['AlgorithmDescription'] = self.algorithm_description + if self.algorithm_name is not None: + result['AlgorithmName'] = self.algorithm_name + if self.display_name is not None: + result['DisplayName'] = self.display_name + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('CheckInfo') is not None: - self.check_info = m.get('CheckInfo') + if m.get('AlgorithmDescription') is not None: + self.algorithm_description = m.get('AlgorithmDescription') + if m.get('AlgorithmName') is not None: + self.algorithm_name = m.get('AlgorithmName') + if m.get('DisplayName') is not None: + self.display_name = m.get('DisplayName') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class CheckInstanceWebTerminalResponseBody(TeaModel): +class CreateAlgorithmResponseBody(TeaModel): def __init__( self, + algorithm_id: str = None, request_id: str = None, ): + self.algorithm_id = algorithm_id self.request_id = request_id def validate(self): @@ -4040,23 +4464,27 @@ def to_map(self): return _map result = dict() + if self.algorithm_id is not None: + result['AlgorithmId'] = self.algorithm_id if self.request_id is not None: result['RequestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() + if m.get('AlgorithmId') is not None: + self.algorithm_id = m.get('AlgorithmId') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') return self -class CheckInstanceWebTerminalResponse(TeaModel): +class CreateAlgorithmResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: CheckInstanceWebTerminalResponseBody = None, + body: CreateAlgorithmResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -4087,23 +4515,46 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = CheckInstanceWebTerminalResponseBody() + temp_model = CreateAlgorithmResponseBody() self.body = temp_model.from_map(m['body']) return self -class CreateAI4DDefaultBucketResponseBody(TeaModel): +class CreateAlgorithmVersionRequest(TeaModel): def __init__( self, - extranet_endpoint: str = None, - intranet_endpoint: str = None, - name: str = None, - request_id: str = None, + algorithm_spec: AlgorithmSpec = None, ): - self.extranet_endpoint = extranet_endpoint - self.intranet_endpoint = intranet_endpoint - self.name = name - self.request_id = request_id + self.algorithm_spec = algorithm_spec + + def validate(self): + if self.algorithm_spec: + self.algorithm_spec.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.algorithm_spec is not None: + result['AlgorithmSpec'] = self.algorithm_spec.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('AlgorithmSpec') is not None: + temp_model = AlgorithmSpec() + self.algorithm_spec = temp_model.from_map(m['AlgorithmSpec']) + return self + + +class CreateAlgorithmVersionShrinkRequest(TeaModel): + def __init__( + self, + algorithm_spec_shrink: str = None, + ): + self.algorithm_spec_shrink = algorithm_spec_shrink def validate(self): pass @@ -4114,35 +4565,56 @@ def to_map(self): return _map result = dict() - if self.extranet_endpoint is not None: - result['ExtranetEndpoint'] = self.extranet_endpoint - if self.intranet_endpoint is not None: - result['IntranetEndpoint'] = self.intranet_endpoint - if self.name is not None: - result['Name'] = self.name - if self.request_id is not None: - result['RequestId'] = self.request_id + if self.algorithm_spec_shrink is not None: + result['AlgorithmSpec'] = self.algorithm_spec_shrink return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ExtranetEndpoint') is not None: - self.extranet_endpoint = m.get('ExtranetEndpoint') - if m.get('IntranetEndpoint') is not None: - self.intranet_endpoint = m.get('IntranetEndpoint') - if m.get('Name') is not None: - self.name = m.get('Name') - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') + if m.get('AlgorithmSpec') is not None: + self.algorithm_spec_shrink = m.get('AlgorithmSpec') return self -class CreateAI4DDefaultBucketResponse(TeaModel): +class CreateAlgorithmVersionResponseBody(TeaModel): + def __init__( + self, + algorithm_id: str = None, + algorithm_version: str = None, + ): + self.algorithm_id = algorithm_id + self.algorithm_version = algorithm_version + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.algorithm_id is not None: + result['AlgorithmId'] = self.algorithm_id + if self.algorithm_version is not None: + result['AlgorithmVersion'] = self.algorithm_version + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('AlgorithmId') is not None: + self.algorithm_id = m.get('AlgorithmId') + if m.get('AlgorithmVersion') is not None: + self.algorithm_version = m.get('AlgorithmVersion') + return self + + +class CreateAlgorithmVersionResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: CreateAI4DDefaultBucketResponseBody = None, + body: CreateAlgorithmVersionResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -4173,24 +4645,34 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = CreateAI4DDefaultBucketResponseBody() + temp_model = CreateAlgorithmVersionResponseBody() self.body = temp_model.from_map(m['body']) return self -class CreateAI4DSerivceRequest(TeaModel): +class CreateComponentRequest(TeaModel): def __init__( self, - inference_spec: Dict[str, Any] = None, - service_type: str = None, + description: str = None, + display_name: str = None, + labels: List[Label] = None, + name: str = None, workspace_id: str = None, ): - self.inference_spec = inference_spec - self.service_type = service_type + # This parameter is required. + self.description = description + self.display_name = display_name + self.labels = labels + # This parameter is required. + self.name = name + # This parameter is required. self.workspace_id = workspace_id def validate(self): - pass + if self.labels: + for k in self.labels: + if k: + k.validate() def to_map(self): _map = super().to_map() @@ -4198,33 +4680,46 @@ def to_map(self): return _map result = dict() - if self.inference_spec is not None: - result['InferenceSpec'] = self.inference_spec - if self.service_type is not None: - result['ServiceType'] = self.service_type + if self.description is not None: + result['Description'] = self.description + if self.display_name is not None: + result['DisplayName'] = self.display_name + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + if self.name is not None: + result['Name'] = self.name if self.workspace_id is not None: result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('InferenceSpec') is not None: - self.inference_spec = m.get('InferenceSpec') - if m.get('ServiceType') is not None: - self.service_type = m.get('ServiceType') + if m.get('Description') is not None: + self.description = m.get('Description') + if m.get('DisplayName') is not None: + self.display_name = m.get('DisplayName') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = Label() + self.labels.append(temp_model.from_map(k)) + if m.get('Name') is not None: + self.name = m.get('Name') if m.get('WorkspaceId') is not None: self.workspace_id = m.get('WorkspaceId') return self -class CreateAI4DSerivceResponseBody(TeaModel): +class CreateComponentResponseBody(TeaModel): def __init__( self, + component_id: str = None, request_id: str = None, - service_name: str = None, ): + self.component_id = component_id self.request_id = request_id - self.service_name = service_name def validate(self): pass @@ -4235,27 +4730,27 @@ def to_map(self): return _map result = dict() + if self.component_id is not None: + result['ComponentId'] = self.component_id if self.request_id is not None: result['RequestId'] = self.request_id - if self.service_name is not None: - result['ServiceName'] = self.service_name return result def from_map(self, m: dict = None): m = m or dict() + if m.get('ComponentId') is not None: + self.component_id = m.get('ComponentId') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('ServiceName') is not None: - self.service_name = m.get('ServiceName') return self -class CreateAI4DSerivceResponse(TeaModel): +class CreateComponentResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: CreateAI4DSerivceResponseBody = None, + body: CreateComponentResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -4286,23 +4781,19 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = CreateAI4DSerivceResponseBody() + temp_model = CreateComponentResponseBody() self.body = temp_model.from_map(m['body']) return self -class CreateAlgorithmRequest(TeaModel): +class CreateComponentVersionRequestLabels(TeaModel): def __init__( self, - algorithm_description: str = None, - algorithm_name: str = None, - display_name: str = None, - workspace_id: str = None, + key: str = None, + value: str = None, ): - self.algorithm_description = algorithm_description - self.algorithm_name = algorithm_name - self.display_name = display_name - self.workspace_id = workspace_id + self.key = key + self.value = value def validate(self): pass @@ -4313,36 +4804,93 @@ def to_map(self): return _map result = dict() - if self.algorithm_description is not None: - result['AlgorithmDescription'] = self.algorithm_description - if self.algorithm_name is not None: - result['AlgorithmName'] = self.algorithm_name - if self.display_name is not None: - result['DisplayName'] = self.display_name - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id + if self.key is not None: + result['Key'] = self.key + if self.value is not None: + result['Value'] = self.value return result def from_map(self, m: dict = None): m = m or dict() - if m.get('AlgorithmDescription') is not None: - self.algorithm_description = m.get('AlgorithmDescription') - if m.get('AlgorithmName') is not None: - self.algorithm_name = m.get('AlgorithmName') - if m.get('DisplayName') is not None: - self.display_name = m.get('DisplayName') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') + if m.get('Key') is not None: + self.key = m.get('Key') + if m.get('Value') is not None: + self.value = m.get('Value') return self -class CreateAlgorithmResponseBody(TeaModel): +class CreateComponentVersionRequest(TeaModel): def __init__( self, - algorithm_id: str = None, + config_dir: Location = None, + description: str = None, + labels: List[CreateComponentVersionRequestLabels] = None, + spec: ComponentSpec = None, + version: str = None, + ): + self.config_dir = config_dir + self.description = description + self.labels = labels + self.spec = spec + self.version = version + + def validate(self): + if self.config_dir: + self.config_dir.validate() + if self.labels: + for k in self.labels: + if k: + k.validate() + if self.spec: + self.spec.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.config_dir is not None: + result['ConfigDir'] = self.config_dir.to_map() + if self.description is not None: + result['Description'] = self.description + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + if self.spec is not None: + result['Spec'] = self.spec.to_map() + if self.version is not None: + result['Version'] = self.version + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('ConfigDir') is not None: + temp_model = Location() + self.config_dir = temp_model.from_map(m['ConfigDir']) + if m.get('Description') is not None: + self.description = m.get('Description') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = CreateComponentVersionRequestLabels() + self.labels.append(temp_model.from_map(k)) + if m.get('Spec') is not None: + temp_model = ComponentSpec() + self.spec = temp_model.from_map(m['Spec']) + if m.get('Version') is not None: + self.version = m.get('Version') + return self + + +class CreateComponentVersionResponseBody(TeaModel): + def __init__( + self, + instance_job_id: str = None, request_id: str = None, ): - self.algorithm_id = algorithm_id + self.instance_job_id = instance_job_id self.request_id = request_id def validate(self): @@ -4354,27 +4902,27 @@ def to_map(self): return _map result = dict() - if self.algorithm_id is not None: - result['AlgorithmId'] = self.algorithm_id + if self.instance_job_id is not None: + result['InstanceJobId'] = self.instance_job_id if self.request_id is not None: result['RequestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('AlgorithmId') is not None: - self.algorithm_id = m.get('AlgorithmId') + if m.get('InstanceJobId') is not None: + self.instance_job_id = m.get('InstanceJobId') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') return self -class CreateAlgorithmResponse(TeaModel): +class CreateComponentVersionResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: CreateAlgorithmResponseBody = None, + body: CreateComponentVersionResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -4405,75 +4953,19 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = CreateAlgorithmResponseBody() + temp_model = CreateComponentVersionResponseBody() self.body = temp_model.from_map(m['body']) return self -class CreateAlgorithmVersionRequest(TeaModel): - def __init__( - self, - algorithm_spec: AlgorithmSpec = None, - ): - self.algorithm_spec = algorithm_spec - - def validate(self): - if self.algorithm_spec: - self.algorithm_spec.validate() - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.algorithm_spec is not None: - result['AlgorithmSpec'] = self.algorithm_spec.to_map() - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('AlgorithmSpec') is not None: - temp_model = AlgorithmSpec() - self.algorithm_spec = temp_model.from_map(m['AlgorithmSpec']) - return self - - -class CreateAlgorithmVersionShrinkRequest(TeaModel): - def __init__( - self, - algorithm_spec_shrink: str = None, - ): - self.algorithm_spec_shrink = algorithm_spec_shrink - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.algorithm_spec_shrink is not None: - result['AlgorithmSpec'] = self.algorithm_spec_shrink - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('AlgorithmSpec') is not None: - self.algorithm_spec_shrink = m.get('AlgorithmSpec') - return self - - -class CreateAlgorithmVersionResponseBody(TeaModel): +class CreateInstanceWebTerminalResponseBody(TeaModel): def __init__( self, - algorithm_id: str = None, - algorithm_version: str = None, + request_id: str = None, + web_terminal_id: str = None, ): - self.algorithm_id = algorithm_id - self.algorithm_version = algorithm_version + self.request_id = request_id + self.web_terminal_id = web_terminal_id def validate(self): pass @@ -4484,27 +4976,27 @@ def to_map(self): return _map result = dict() - if self.algorithm_id is not None: - result['AlgorithmId'] = self.algorithm_id - if self.algorithm_version is not None: - result['AlgorithmVersion'] = self.algorithm_version + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.web_terminal_id is not None: + result['WebTerminalId'] = self.web_terminal_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('AlgorithmId') is not None: - self.algorithm_id = m.get('AlgorithmId') - if m.get('AlgorithmVersion') is not None: - self.algorithm_version = m.get('AlgorithmVersion') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('WebTerminalId') is not None: + self.web_terminal_id = m.get('WebTerminalId') return self -class CreateAlgorithmVersionResponse(TeaModel): +class CreateInstanceWebTerminalResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: CreateAlgorithmVersionResponseBody = None, + body: CreateInstanceWebTerminalResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -4535,31 +5027,45 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = CreateAlgorithmVersionResponseBody() + temp_model = CreateInstanceWebTerminalResponseBody() self.body = temp_model.from_map(m['body']) return self -class CreateComponentRequest(TeaModel): +class CreateQuotaRequest(TeaModel): def __init__( self, + allocate_strategy: str = None, description: str = None, - display_name: str = None, labels: List[Label] = None, - name: str = None, - workspace_id: str = None, + min: ResourceSpec = None, + parent_quota_id: str = None, + queue_strategy: str = None, + quota_config: QuotaConfig = None, + quota_name: str = None, + resource_group_ids: List[str] = None, + resource_type: str = None, ): + self.allocate_strategy = allocate_strategy self.description = description - self.display_name = display_name self.labels = labels - self.name = name - self.workspace_id = workspace_id + self.min = min + self.parent_quota_id = parent_quota_id + self.queue_strategy = queue_strategy + self.quota_config = quota_config + self.quota_name = quota_name + self.resource_group_ids = resource_group_ids + self.resource_type = resource_type def validate(self): if self.labels: for k in self.labels: if k: k.validate() + if self.min: + self.min.validate() + if self.quota_config: + self.quota_config.validate() def to_map(self): _map = super().to_map() @@ -4567,45 +5073,68 @@ def to_map(self): return _map result = dict() + if self.allocate_strategy is not None: + result['AllocateStrategy'] = self.allocate_strategy if self.description is not None: result['Description'] = self.description - if self.display_name is not None: - result['DisplayName'] = self.display_name result['Labels'] = [] if self.labels is not None: for k in self.labels: result['Labels'].append(k.to_map() if k else None) - if self.name is not None: - result['Name'] = self.name - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('Description') is not None: - self.description = m.get('Description') - if m.get('DisplayName') is not None: - self.display_name = m.get('DisplayName') - self.labels = [] - if m.get('Labels') is not None: - for k in m.get('Labels'): - temp_model = Label() - self.labels.append(temp_model.from_map(k)) - if m.get('Name') is not None: - self.name = m.get('Name') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') + if self.min is not None: + result['Min'] = self.min.to_map() + if self.parent_quota_id is not None: + result['ParentQuotaId'] = self.parent_quota_id + if self.queue_strategy is not None: + result['QueueStrategy'] = self.queue_strategy + if self.quota_config is not None: + result['QuotaConfig'] = self.quota_config.to_map() + if self.quota_name is not None: + result['QuotaName'] = self.quota_name + if self.resource_group_ids is not None: + result['ResourceGroupIds'] = self.resource_group_ids + if self.resource_type is not None: + result['ResourceType'] = self.resource_type + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('AllocateStrategy') is not None: + self.allocate_strategy = m.get('AllocateStrategy') + if m.get('Description') is not None: + self.description = m.get('Description') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = Label() + self.labels.append(temp_model.from_map(k)) + if m.get('Min') is not None: + temp_model = ResourceSpec() + self.min = temp_model.from_map(m['Min']) + if m.get('ParentQuotaId') is not None: + self.parent_quota_id = m.get('ParentQuotaId') + if m.get('QueueStrategy') is not None: + self.queue_strategy = m.get('QueueStrategy') + if m.get('QuotaConfig') is not None: + temp_model = QuotaConfig() + self.quota_config = temp_model.from_map(m['QuotaConfig']) + if m.get('QuotaName') is not None: + self.quota_name = m.get('QuotaName') + if m.get('ResourceGroupIds') is not None: + self.resource_group_ids = m.get('ResourceGroupIds') + if m.get('ResourceType') is not None: + self.resource_type = m.get('ResourceType') return self -class CreateComponentResponseBody(TeaModel): +class CreateQuotaResponseBody(TeaModel): def __init__( self, - component_id: str = None, + quota_id: str = None, request_id: str = None, ): - self.component_id = component_id + # Quota Id + self.quota_id = quota_id self.request_id = request_id def validate(self): @@ -4617,27 +5146,27 @@ def to_map(self): return _map result = dict() - if self.component_id is not None: - result['ComponentId'] = self.component_id + if self.quota_id is not None: + result['QuotaId'] = self.quota_id if self.request_id is not None: result['RequestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ComponentId') is not None: - self.component_id = m.get('ComponentId') + if m.get('QuotaId') is not None: + self.quota_id = m.get('QuotaId') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') return self -class CreateComponentResponse(TeaModel): +class CreateQuotaResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: CreateComponentResponseBody = None, + body: CreateQuotaResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -4668,12 +5197,12 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = CreateComponentResponseBody() + temp_model = CreateQuotaResponseBody() self.body = temp_model.from_map(m['body']) return self -class CreateComponentVersionRequestLabels(TeaModel): +class CreateResourceGroupRequestTag(TeaModel): def __init__( self, key: str = None, @@ -4706,30 +5235,30 @@ def from_map(self, m: dict = None): return self -class CreateComponentVersionRequest(TeaModel): +class CreateResourceGroupRequest(TeaModel): def __init__( self, - config_dir: Location = None, + computing_resource_provider: str = None, description: str = None, - labels: List[CreateComponentVersionRequestLabels] = None, - spec: ComponentSpec = None, - version: str = None, + name: str = None, + resource_type: str = None, + tag: List[CreateResourceGroupRequestTag] = None, + user_vpc: UserVpc = None, ): - self.config_dir = config_dir + self.computing_resource_provider = computing_resource_provider self.description = description - self.labels = labels - self.spec = spec - self.version = version + self.name = name + self.resource_type = resource_type + self.tag = tag + self.user_vpc = user_vpc def validate(self): - if self.config_dir: - self.config_dir.validate() - if self.labels: - for k in self.labels: + if self.tag: + for k in self.tag: if k: k.validate() - if self.spec: - self.spec.validate() + if self.user_vpc: + self.user_vpc.validate() def to_map(self): _map = super().to_map() @@ -4737,48 +5266,51 @@ def to_map(self): return _map result = dict() - if self.config_dir is not None: - result['ConfigDir'] = self.config_dir.to_map() + if self.computing_resource_provider is not None: + result['ComputingResourceProvider'] = self.computing_resource_provider if self.description is not None: result['Description'] = self.description - result['Labels'] = [] - if self.labels is not None: - for k in self.labels: - result['Labels'].append(k.to_map() if k else None) - if self.spec is not None: - result['Spec'] = self.spec.to_map() - if self.version is not None: - result['Version'] = self.version + if self.name is not None: + result['Name'] = self.name + if self.resource_type is not None: + result['ResourceType'] = self.resource_type + result['Tag'] = [] + if self.tag is not None: + for k in self.tag: + result['Tag'].append(k.to_map() if k else None) + if self.user_vpc is not None: + result['UserVpc'] = self.user_vpc.to_map() return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ConfigDir') is not None: - temp_model = Location() - self.config_dir = temp_model.from_map(m['ConfigDir']) + if m.get('ComputingResourceProvider') is not None: + self.computing_resource_provider = m.get('ComputingResourceProvider') if m.get('Description') is not None: self.description = m.get('Description') - self.labels = [] - if m.get('Labels') is not None: - for k in m.get('Labels'): - temp_model = CreateComponentVersionRequestLabels() - self.labels.append(temp_model.from_map(k)) - if m.get('Spec') is not None: - temp_model = ComponentSpec() - self.spec = temp_model.from_map(m['Spec']) - if m.get('Version') is not None: - self.version = m.get('Version') + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('ResourceType') is not None: + self.resource_type = m.get('ResourceType') + self.tag = [] + if m.get('Tag') is not None: + for k in m.get('Tag'): + temp_model = CreateResourceGroupRequestTag() + self.tag.append(temp_model.from_map(k)) + if m.get('UserVpc') is not None: + temp_model = UserVpc() + self.user_vpc = temp_model.from_map(m['UserVpc']) return self -class CreateComponentVersionResponseBody(TeaModel): +class CreateResourceGroupResponseBody(TeaModel): def __init__( self, - instance_job_id: str = None, request_id: str = None, + resource_group_id: str = None, ): - self.instance_job_id = instance_job_id self.request_id = request_id + self.resource_group_id = resource_group_id def validate(self): pass @@ -4789,27 +5321,27 @@ def to_map(self): return _map result = dict() - if self.instance_job_id is not None: - result['InstanceJobId'] = self.instance_job_id if self.request_id is not None: result['RequestId'] = self.request_id + if self.resource_group_id is not None: + result['ResourceGroupID'] = self.resource_group_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('InstanceJobId') is not None: - self.instance_job_id = m.get('InstanceJobId') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') + if m.get('ResourceGroupID') is not None: + self.resource_group_id = m.get('ResourceGroupID') return self -class CreateComponentVersionResponse(TeaModel): +class CreateResourceGroupResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: CreateComponentVersionResponseBody = None, + body: CreateResourceGroupResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -4840,19 +5372,19 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = CreateComponentVersionResponseBody() + temp_model = CreateResourceGroupResponseBody() self.body = temp_model.from_map(m['body']) return self -class CreateInstanceWebTerminalResponseBody(TeaModel): +class CreateResourceGroupMachineGroupRequestTag(TeaModel): def __init__( self, - request_id: str = None, - web_terminal_id: str = None, + key: str = None, + value: str = None, ): - self.request_id = request_id - self.web_terminal_id = web_terminal_id + self.key = key + self.value = value def validate(self): pass @@ -4863,35 +5395,45 @@ def to_map(self): return _map result = dict() - if self.request_id is not None: - result['RequestId'] = self.request_id - if self.web_terminal_id is not None: - result['WebTerminalId'] = self.web_terminal_id + if self.key is not None: + result['Key'] = self.key + if self.value is not None: + result['Value'] = self.value return result def from_map(self, m: dict = None): m = m or dict() - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - if m.get('WebTerminalId') is not None: - self.web_terminal_id = m.get('WebTerminalId') + if m.get('Key') is not None: + self.key = m.get('Key') + if m.get('Value') is not None: + self.value = m.get('Value') return self -class CreateInstanceWebTerminalResponse(TeaModel): +class CreateResourceGroupMachineGroupRequest(TeaModel): def __init__( self, - headers: Dict[str, str] = None, - status_code: int = None, - body: CreateInstanceWebTerminalResponseBody = None, + ecs_count: int = None, + ecs_spec: str = None, + name: str = None, + payment_duration: str = None, + payment_duration_unit: str = None, + payment_type: str = None, + tag: List[CreateResourceGroupMachineGroupRequestTag] = None, ): - self.headers = headers - self.status_code = status_code - self.body = body + self.ecs_count = ecs_count + self.ecs_spec = ecs_spec + self.name = name + self.payment_duration = payment_duration + self.payment_duration_unit = payment_duration_unit + self.payment_type = payment_type + self.tag = tag def validate(self): - if self.body: - self.body.validate() + if self.tag: + for k in self.tag: + if k: + k.validate() def to_map(self): _map = super().to_map() @@ -4899,173 +5441,53 @@ def to_map(self): return _map result = dict() - if self.headers is not None: - result['headers'] = self.headers - if self.status_code is not None: - result['statusCode'] = self.status_code - if self.body is not None: - result['body'] = self.body.to_map() + if self.ecs_count is not None: + result['EcsCount'] = self.ecs_count + if self.ecs_spec is not None: + result['EcsSpec'] = self.ecs_spec + if self.name is not None: + result['Name'] = self.name + if self.payment_duration is not None: + result['PaymentDuration'] = self.payment_duration + if self.payment_duration_unit is not None: + result['PaymentDurationUnit'] = self.payment_duration_unit + if self.payment_type is not None: + result['PaymentType'] = self.payment_type + result['Tag'] = [] + if self.tag is not None: + for k in self.tag: + result['Tag'].append(k.to_map() if k else None) return result def from_map(self, m: dict = None): m = m or dict() - if m.get('headers') is not None: - self.headers = m.get('headers') - if m.get('statusCode') is not None: - self.status_code = m.get('statusCode') - if m.get('body') is not None: - temp_model = CreateInstanceWebTerminalResponseBody() - self.body = temp_model.from_map(m['body']) - return self - - -class CreateLLMProjectRequestLabels(TeaModel): - def __init__( - self, - key: str = None, - value: str = None, - ): - self.key = key - self.value = value - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.key is not None: - result['Key'] = self.key - if self.value is not None: - result['Value'] = self.value - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('Key') is not None: - self.key = m.get('Key') - if m.get('Value') is not None: - self.value = m.get('Value') - return self - - -class CreateLLMProjectRequestRuntime(TeaModel): - def __init__( - self, - runtime_id: str = None, - runtime_type: str = None, - ): - self.runtime_id = runtime_id - self.runtime_type = runtime_type - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.runtime_id is not None: - result['RuntimeId'] = self.runtime_id - if self.runtime_type is not None: - result['RuntimeType'] = self.runtime_type - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('RuntimeId') is not None: - self.runtime_id = m.get('RuntimeId') - if m.get('RuntimeType') is not None: - self.runtime_type = m.get('RuntimeType') - return self - - -class CreateLLMProjectRequest(TeaModel): - def __init__( - self, - labels: List[CreateLLMProjectRequestLabels] = None, - project_description: str = None, - project_name: str = None, - project_type: str = None, - root_path: str = None, - runtime: CreateLLMProjectRequestRuntime = None, - workspace_id: str = None, - ): - self.labels = labels - self.project_description = project_description - self.project_name = project_name - self.project_type = project_type - self.root_path = root_path - self.runtime = runtime - self.workspace_id = workspace_id - - def validate(self): - if self.labels: - for k in self.labels: - if k: - k.validate() - if self.runtime: - self.runtime.validate() - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - result['Labels'] = [] - if self.labels is not None: - for k in self.labels: - result['Labels'].append(k.to_map() if k else None) - if self.project_description is not None: - result['ProjectDescription'] = self.project_description - if self.project_name is not None: - result['ProjectName'] = self.project_name - if self.project_type is not None: - result['ProjectType'] = self.project_type - if self.root_path is not None: - result['RootPath'] = self.root_path - if self.runtime is not None: - result['Runtime'] = self.runtime.to_map() - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id - return result - - def from_map(self, m: dict = None): - m = m or dict() - self.labels = [] - if m.get('Labels') is not None: - for k in m.get('Labels'): - temp_model = CreateLLMProjectRequestLabels() - self.labels.append(temp_model.from_map(k)) - if m.get('ProjectDescription') is not None: - self.project_description = m.get('ProjectDescription') - if m.get('ProjectName') is not None: - self.project_name = m.get('ProjectName') - if m.get('ProjectType') is not None: - self.project_type = m.get('ProjectType') - if m.get('RootPath') is not None: - self.root_path = m.get('RootPath') - if m.get('Runtime') is not None: - temp_model = CreateLLMProjectRequestRuntime() - self.runtime = temp_model.from_map(m['Runtime']) - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') + if m.get('EcsCount') is not None: + self.ecs_count = m.get('EcsCount') + if m.get('EcsSpec') is not None: + self.ecs_spec = m.get('EcsSpec') + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('PaymentDuration') is not None: + self.payment_duration = m.get('PaymentDuration') + if m.get('PaymentDurationUnit') is not None: + self.payment_duration_unit = m.get('PaymentDurationUnit') + if m.get('PaymentType') is not None: + self.payment_type = m.get('PaymentType') + self.tag = [] + if m.get('Tag') is not None: + for k in m.get('Tag'): + temp_model = CreateResourceGroupMachineGroupRequestTag() + self.tag.append(temp_model.from_map(k)) return self -class CreateLLMProjectResponseBody(TeaModel): +class CreateResourceGroupMachineGroupResponseBody(TeaModel): def __init__( self, - project_id: str = None, + machine_group_id: str = None, request_id: str = None, ): - self.project_id = project_id + self.machine_group_id = machine_group_id self.request_id = request_id def validate(self): @@ -5077,27 +5499,27 @@ def to_map(self): return _map result = dict() - if self.project_id is not None: - result['ProjectId'] = self.project_id + if self.machine_group_id is not None: + result['MachineGroupID'] = self.machine_group_id if self.request_id is not None: result['RequestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ProjectId') is not None: - self.project_id = m.get('ProjectId') + if m.get('MachineGroupID') is not None: + self.machine_group_id = m.get('MachineGroupID') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') return self -class CreateLLMProjectResponse(TeaModel): +class CreateResourceGroupMachineGroupResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: CreateLLMProjectResponseBody = None, + body: CreateResourceGroupMachineGroupResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -5128,12 +5550,12 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = CreateLLMProjectResponseBody() + temp_model = CreateResourceGroupMachineGroupResponseBody() self.body = temp_model.from_map(m['body']) return self -class CreateLLMServiceIdentityRoleRequest(TeaModel): +class CreateServiceIdentityRoleRequest(TeaModel): def __init__( self, role_name: str = None, @@ -5160,7 +5582,7 @@ def from_map(self, m: dict = None): return self -class CreateLLMServiceIdentityRoleResponseBody(TeaModel): +class CreateServiceIdentityRoleResponseBody(TeaModel): def __init__( self, request_id: str = None, @@ -5193,12 +5615,12 @@ def from_map(self, m: dict = None): return self -class CreateLLMServiceIdentityRoleResponse(TeaModel): +class CreateServiceIdentityRoleResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: CreateLLMServiceIdentityRoleResponseBody = None, + body: CreateServiceIdentityRoleResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -5229,19 +5651,25 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = CreateLLMServiceIdentityRoleResponseBody() + temp_model = CreateServiceIdentityRoleResponseBody() self.body = temp_model.from_map(m['body']) return self -class CreateLLMSnapshotRequestStorage(TeaModel): +class CreateTrainingJobRequestComputeResourceInstanceSpec(TeaModel): def __init__( self, - location: str = None, - type: str = None, + cpu: str = None, + gpu: str = None, + gputype: str = None, + memory: str = None, + shared_memory: str = None, ): - self.location = location - self.type = type + self.cpu = cpu + self.gpu = gpu + self.gputype = gputype + self.memory = memory + self.shared_memory = shared_memory def validate(self): pass @@ -5252,31 +5680,44 @@ def to_map(self): return _map result = dict() - if self.location is not None: - result['Location'] = self.location - if self.type is not None: - result['Type'] = self.type + if self.cpu is not None: + result['CPU'] = self.cpu + if self.gpu is not None: + result['GPU'] = self.gpu + if self.gputype is not None: + result['GPUType'] = self.gputype + if self.memory is not None: + result['Memory'] = self.memory + if self.shared_memory is not None: + result['SharedMemory'] = self.shared_memory return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Location') is not None: - self.location = m.get('Location') - if m.get('Type') is not None: - self.type = m.get('Type') + if m.get('CPU') is not None: + self.cpu = m.get('CPU') + if m.get('GPU') is not None: + self.gpu = m.get('GPU') + if m.get('GPUType') is not None: + self.gputype = m.get('GPUType') + if m.get('Memory') is not None: + self.memory = m.get('Memory') + if m.get('SharedMemory') is not None: + self.shared_memory = m.get('SharedMemory') return self -class CreateLLMSnapshotRequest(TeaModel): +class CreateTrainingJobRequestComputeResourceSpotSpec(TeaModel): def __init__( self, - storage: CreateLLMSnapshotRequestStorage = None, + spot_discount_limit: float = None, + spot_strategy: str = None, ): - self.storage = storage + self.spot_discount_limit = spot_discount_limit + self.spot_strategy = spot_strategy def validate(self): - if self.storage: - self.storage.validate() + pass def to_map(self): _map = super().to_map() @@ -5284,31 +5725,45 @@ def to_map(self): return _map result = dict() - if self.storage is not None: - result['Storage'] = self.storage.to_map() + if self.spot_discount_limit is not None: + result['SpotDiscountLimit'] = self.spot_discount_limit + if self.spot_strategy is not None: + result['SpotStrategy'] = self.spot_strategy return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Storage') is not None: - temp_model = CreateLLMSnapshotRequestStorage() - self.storage = temp_model.from_map(m['Storage']) + if m.get('SpotDiscountLimit') is not None: + self.spot_discount_limit = m.get('SpotDiscountLimit') + if m.get('SpotStrategy') is not None: + self.spot_strategy = m.get('SpotStrategy') return self -class CreateLLMSnapshotResponseBody(TeaModel): +class CreateTrainingJobRequestComputeResource(TeaModel): def __init__( self, - pipeline_run_id: str = None, - request_id: str = None, - snapshot_id: str = None, + ecs_count: int = None, + ecs_spec: str = None, + instance_count: int = None, + instance_spec: CreateTrainingJobRequestComputeResourceInstanceSpec = None, + resource_id: str = None, + spot_spec: CreateTrainingJobRequestComputeResourceSpotSpec = None, + use_spot_instance: bool = None, ): - self.pipeline_run_id = pipeline_run_id - self.request_id = request_id - self.snapshot_id = snapshot_id + self.ecs_count = ecs_count + self.ecs_spec = ecs_spec + self.instance_count = instance_count + self.instance_spec = instance_spec + self.resource_id = resource_id + self.spot_spec = spot_spec + self.use_spot_instance = use_spot_instance def validate(self): - pass + if self.instance_spec: + self.instance_spec.validate() + if self.spot_spec: + self.spot_spec.validate() def to_map(self): _map = super().to_map() @@ -5316,39 +5771,52 @@ def to_map(self): return _map result = dict() - if self.pipeline_run_id is not None: - result['PipelineRunId'] = self.pipeline_run_id - if self.request_id is not None: - result['RequestId'] = self.request_id - if self.snapshot_id is not None: - result['SnapshotId'] = self.snapshot_id + if self.ecs_count is not None: + result['EcsCount'] = self.ecs_count + if self.ecs_spec is not None: + result['EcsSpec'] = self.ecs_spec + if self.instance_count is not None: + result['InstanceCount'] = self.instance_count + if self.instance_spec is not None: + result['InstanceSpec'] = self.instance_spec.to_map() + if self.resource_id is not None: + result['ResourceId'] = self.resource_id + if self.spot_spec is not None: + result['SpotSpec'] = self.spot_spec.to_map() + if self.use_spot_instance is not None: + result['UseSpotInstance'] = self.use_spot_instance return result def from_map(self, m: dict = None): m = m or dict() - if m.get('PipelineRunId') is not None: - self.pipeline_run_id = m.get('PipelineRunId') - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - if m.get('SnapshotId') is not None: - self.snapshot_id = m.get('SnapshotId') + if m.get('EcsCount') is not None: + self.ecs_count = m.get('EcsCount') + if m.get('EcsSpec') is not None: + self.ecs_spec = m.get('EcsSpec') + if m.get('InstanceCount') is not None: + self.instance_count = m.get('InstanceCount') + if m.get('InstanceSpec') is not None: + temp_model = CreateTrainingJobRequestComputeResourceInstanceSpec() + self.instance_spec = temp_model.from_map(m['InstanceSpec']) + if m.get('ResourceId') is not None: + self.resource_id = m.get('ResourceId') + if m.get('SpotSpec') is not None: + temp_model = CreateTrainingJobRequestComputeResourceSpotSpec() + self.spot_spec = temp_model.from_map(m['SpotSpec']) + if m.get('UseSpotInstance') is not None: + self.use_spot_instance = m.get('UseSpotInstance') return self -class CreateLLMSnapshotResponse(TeaModel): +class CreateTrainingJobRequestExperimentConfig(TeaModel): def __init__( self, - headers: Dict[str, str] = None, - status_code: int = None, - body: CreateLLMSnapshotResponseBody = None, + experiment_id: str = None, ): - self.headers = headers - self.status_code = status_code - self.body = body + self.experiment_id = experiment_id def validate(self): - if self.body: - self.body.validate() + pass def to_map(self): _map = super().to_map() @@ -5356,130 +5824,25 @@ def to_map(self): return _map result = dict() - if self.headers is not None: - result['headers'] = self.headers - if self.status_code is not None: - result['statusCode'] = self.status_code - if self.body is not None: - result['body'] = self.body.to_map() + if self.experiment_id is not None: + result['ExperimentId'] = self.experiment_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('headers') is not None: - self.headers = m.get('headers') - if m.get('statusCode') is not None: - self.status_code = m.get('statusCode') - if m.get('body') is not None: - temp_model = CreateLLMSnapshotResponseBody() - self.body = temp_model.from_map(m['body']) + if m.get('ExperimentId') is not None: + self.experiment_id = m.get('ExperimentId') return self -class CreateQuotaRequest(TeaModel): +class CreateTrainingJobRequestHyperParameters(TeaModel): def __init__( self, - allocate_strategy: str = None, - description: str = None, - labels: List[Label] = None, - min: ResourceSpec = None, - parent_quota_id: str = None, - queue_strategy: str = None, - quota_config: QuotaConfig = None, - quota_name: str = None, - resource_group_ids: List[str] = None, - resource_type: str = None, + name: str = None, + value: str = None, ): - self.allocate_strategy = allocate_strategy - self.description = description - self.labels = labels - self.min = min - self.parent_quota_id = parent_quota_id - self.queue_strategy = queue_strategy - self.quota_config = quota_config - self.quota_name = quota_name - self.resource_group_ids = resource_group_ids - self.resource_type = resource_type - - def validate(self): - if self.labels: - for k in self.labels: - if k: - k.validate() - if self.min: - self.min.validate() - if self.quota_config: - self.quota_config.validate() - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.allocate_strategy is not None: - result['AllocateStrategy'] = self.allocate_strategy - if self.description is not None: - result['Description'] = self.description - result['Labels'] = [] - if self.labels is not None: - for k in self.labels: - result['Labels'].append(k.to_map() if k else None) - if self.min is not None: - result['Min'] = self.min.to_map() - if self.parent_quota_id is not None: - result['ParentQuotaId'] = self.parent_quota_id - if self.queue_strategy is not None: - result['QueueStrategy'] = self.queue_strategy - if self.quota_config is not None: - result['QuotaConfig'] = self.quota_config.to_map() - if self.quota_name is not None: - result['QuotaName'] = self.quota_name - if self.resource_group_ids is not None: - result['ResourceGroupIds'] = self.resource_group_ids - if self.resource_type is not None: - result['ResourceType'] = self.resource_type - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('AllocateStrategy') is not None: - self.allocate_strategy = m.get('AllocateStrategy') - if m.get('Description') is not None: - self.description = m.get('Description') - self.labels = [] - if m.get('Labels') is not None: - for k in m.get('Labels'): - temp_model = Label() - self.labels.append(temp_model.from_map(k)) - if m.get('Min') is not None: - temp_model = ResourceSpec() - self.min = temp_model.from_map(m['Min']) - if m.get('ParentQuotaId') is not None: - self.parent_quota_id = m.get('ParentQuotaId') - if m.get('QueueStrategy') is not None: - self.queue_strategy = m.get('QueueStrategy') - if m.get('QuotaConfig') is not None: - temp_model = QuotaConfig() - self.quota_config = temp_model.from_map(m['QuotaConfig']) - if m.get('QuotaName') is not None: - self.quota_name = m.get('QuotaName') - if m.get('ResourceGroupIds') is not None: - self.resource_group_ids = m.get('ResourceGroupIds') - if m.get('ResourceType') is not None: - self.resource_type = m.get('ResourceType') - return self - - -class CreateQuotaResponseBody(TeaModel): - def __init__( - self, - quota_id: str = None, - request_id: str = None, - ): - # Quota Id - self.quota_id = quota_id - self.request_id = request_id + self.name = name + self.value = value def validate(self): pass @@ -5490,35 +5853,34 @@ def to_map(self): return _map result = dict() - if self.quota_id is not None: - result['QuotaId'] = self.quota_id - if self.request_id is not None: - result['RequestId'] = self.request_id + if self.name is not None: + result['Name'] = self.name + if self.value is not None: + result['Value'] = self.value return result def from_map(self, m: dict = None): m = m or dict() - if m.get('QuotaId') is not None: - self.quota_id = m.get('QuotaId') - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('Value') is not None: + self.value = m.get('Value') return self -class CreateQuotaResponse(TeaModel): +class CreateTrainingJobRequestInputChannels(TeaModel): def __init__( self, - headers: Dict[str, str] = None, - status_code: int = None, - body: CreateQuotaResponseBody = None, + dataset_id: str = None, + input_uri: str = None, + name: str = None, ): - self.headers = headers - self.status_code = status_code - self.body = body + self.dataset_id = dataset_id + self.input_uri = input_uri + self.name = name def validate(self): - if self.body: - self.body.validate() + pass def to_map(self): _map = super().to_map() @@ -5526,27 +5888,26 @@ def to_map(self): return _map result = dict() - if self.headers is not None: - result['headers'] = self.headers - if self.status_code is not None: - result['statusCode'] = self.status_code - if self.body is not None: - result['body'] = self.body.to_map() + if self.dataset_id is not None: + result['DatasetId'] = self.dataset_id + if self.input_uri is not None: + result['InputUri'] = self.input_uri + if self.name is not None: + result['Name'] = self.name return result def from_map(self, m: dict = None): m = m or dict() - if m.get('headers') is not None: - self.headers = m.get('headers') - if m.get('statusCode') is not None: - self.status_code = m.get('statusCode') - if m.get('body') is not None: - temp_model = CreateQuotaResponseBody() - self.body = temp_model.from_map(m['body']) + if m.get('DatasetId') is not None: + self.dataset_id = m.get('DatasetId') + if m.get('InputUri') is not None: + self.input_uri = m.get('InputUri') + if m.get('Name') is not None: + self.name = m.get('Name') return self -class CreateResourceGroupRequestTag(TeaModel): +class CreateTrainingJobRequestLabels(TeaModel): def __init__( self, key: str = None, @@ -5579,30 +5940,19 @@ def from_map(self, m: dict = None): return self -class CreateResourceGroupRequest(TeaModel): +class CreateTrainingJobRequestOutputChannels(TeaModel): def __init__( self, - computing_resource_provider: str = None, - description: str = None, + dataset_id: str = None, name: str = None, - resource_type: str = None, - tag: List[CreateResourceGroupRequestTag] = None, - user_vpc: UserVpc = None, + output_uri: str = None, ): - self.computing_resource_provider = computing_resource_provider - self.description = description + self.dataset_id = dataset_id self.name = name - self.resource_type = resource_type - self.tag = tag - self.user_vpc = user_vpc + self.output_uri = output_uri def validate(self): - if self.tag: - for k in self.tag: - if k: - k.validate() - if self.user_vpc: - self.user_vpc.validate() + pass def to_map(self): _map = super().to_map() @@ -5610,51 +5960,31 @@ def to_map(self): return _map result = dict() - if self.computing_resource_provider is not None: - result['ComputingResourceProvider'] = self.computing_resource_provider - if self.description is not None: - result['Description'] = self.description + if self.dataset_id is not None: + result['DatasetId'] = self.dataset_id if self.name is not None: result['Name'] = self.name - if self.resource_type is not None: - result['ResourceType'] = self.resource_type - result['Tag'] = [] - if self.tag is not None: - for k in self.tag: - result['Tag'].append(k.to_map() if k else None) - if self.user_vpc is not None: - result['UserVpc'] = self.user_vpc.to_map() + if self.output_uri is not None: + result['OutputUri'] = self.output_uri return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ComputingResourceProvider') is not None: - self.computing_resource_provider = m.get('ComputingResourceProvider') - if m.get('Description') is not None: - self.description = m.get('Description') + if m.get('DatasetId') is not None: + self.dataset_id = m.get('DatasetId') if m.get('Name') is not None: self.name = m.get('Name') - if m.get('ResourceType') is not None: - self.resource_type = m.get('ResourceType') - self.tag = [] - if m.get('Tag') is not None: - for k in m.get('Tag'): - temp_model = CreateResourceGroupRequestTag() - self.tag.append(temp_model.from_map(k)) - if m.get('UserVpc') is not None: - temp_model = UserVpc() - self.user_vpc = temp_model.from_map(m['UserVpc']) + if m.get('OutputUri') is not None: + self.output_uri = m.get('OutputUri') return self -class CreateResourceGroupResponseBody(TeaModel): +class CreateTrainingJobRequestScheduler(TeaModel): def __init__( self, - request_id: str = None, - resource_group_id: str = None, + max_running_time_in_seconds: int = None, ): - self.request_id = request_id - self.resource_group_id = resource_group_id + self.max_running_time_in_seconds = max_running_time_in_seconds def validate(self): pass @@ -5665,35 +5995,32 @@ def to_map(self): return _map result = dict() - if self.request_id is not None: - result['RequestId'] = self.request_id - if self.resource_group_id is not None: - result['ResourceGroupID'] = self.resource_group_id + if self.max_running_time_in_seconds is not None: + result['MaxRunningTimeInSeconds'] = self.max_running_time_in_seconds return result def from_map(self, m: dict = None): m = m or dict() - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - if m.get('ResourceGroupID') is not None: - self.resource_group_id = m.get('ResourceGroupID') + if m.get('MaxRunningTimeInSeconds') is not None: + self.max_running_time_in_seconds = m.get('MaxRunningTimeInSeconds') return self -class CreateResourceGroupResponse(TeaModel): +class CreateTrainingJobRequestSettings(TeaModel): def __init__( self, - headers: Dict[str, str] = None, - status_code: int = None, - body: CreateResourceGroupResponseBody = None, + aimaster_type: str = None, + enable_error_monitoring_in_aimaster: bool = None, + error_monitoring_args: str = None, + priority: int = None, ): - self.headers = headers - self.status_code = status_code - self.body = body + self.aimaster_type = aimaster_type + self.enable_error_monitoring_in_aimaster = enable_error_monitoring_in_aimaster + self.error_monitoring_args = error_monitoring_args + self.priority = priority def validate(self): - if self.body: - self.body.validate() + pass def to_map(self): _map = super().to_map() @@ -5701,34 +6028,43 @@ def to_map(self): return _map result = dict() - if self.headers is not None: - result['headers'] = self.headers - if self.status_code is not None: - result['statusCode'] = self.status_code - if self.body is not None: - result['body'] = self.body.to_map() + if self.aimaster_type is not None: + result['AIMasterType'] = self.aimaster_type + if self.enable_error_monitoring_in_aimaster is not None: + result['EnableErrorMonitoringInAIMaster'] = self.enable_error_monitoring_in_aimaster + if self.error_monitoring_args is not None: + result['ErrorMonitoringArgs'] = self.error_monitoring_args + if self.priority is not None: + result['Priority'] = self.priority return result def from_map(self, m: dict = None): m = m or dict() - if m.get('headers') is not None: - self.headers = m.get('headers') - if m.get('statusCode') is not None: - self.status_code = m.get('statusCode') - if m.get('body') is not None: - temp_model = CreateResourceGroupResponseBody() - self.body = temp_model.from_map(m['body']) + if m.get('AIMasterType') is not None: + self.aimaster_type = m.get('AIMasterType') + if m.get('EnableErrorMonitoringInAIMaster') is not None: + self.enable_error_monitoring_in_aimaster = m.get('EnableErrorMonitoringInAIMaster') + if m.get('ErrorMonitoringArgs') is not None: + self.error_monitoring_args = m.get('ErrorMonitoringArgs') + if m.get('Priority') is not None: + self.priority = m.get('Priority') return self -class CreateResourceGroupMachineGroupRequestTag(TeaModel): +class CreateTrainingJobRequestUserVpc(TeaModel): def __init__( self, - key: str = None, - value: str = None, + default_route: str = None, + extended_cidrs: List[str] = None, + security_group_id: str = None, + switch_id: str = None, + vpc_id: str = None, ): - self.key = key - self.value = value + self.default_route = default_route + self.extended_cidrs = extended_cidrs + self.security_group_id = security_group_id + self.switch_id = switch_id + self.vpc_id = vpc_id def validate(self): pass @@ -5739,45 +6075,113 @@ def to_map(self): return _map result = dict() - if self.key is not None: - result['Key'] = self.key - if self.value is not None: - result['Value'] = self.value + if self.default_route is not None: + result['DefaultRoute'] = self.default_route + if self.extended_cidrs is not None: + result['ExtendedCIDRs'] = self.extended_cidrs + if self.security_group_id is not None: + result['SecurityGroupId'] = self.security_group_id + if self.switch_id is not None: + result['SwitchId'] = self.switch_id + if self.vpc_id is not None: + result['VpcId'] = self.vpc_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Key') is not None: - self.key = m.get('Key') - if m.get('Value') is not None: - self.value = m.get('Value') + if m.get('DefaultRoute') is not None: + self.default_route = m.get('DefaultRoute') + if m.get('ExtendedCIDRs') is not None: + self.extended_cidrs = m.get('ExtendedCIDRs') + if m.get('SecurityGroupId') is not None: + self.security_group_id = m.get('SecurityGroupId') + if m.get('SwitchId') is not None: + self.switch_id = m.get('SwitchId') + if m.get('VpcId') is not None: + self.vpc_id = m.get('VpcId') return self -class CreateResourceGroupMachineGroupRequest(TeaModel): +class CreateTrainingJobRequest(TeaModel): def __init__( self, - ecs_count: int = None, - ecs_spec: str = None, - name: str = None, - payment_duration: str = None, - payment_duration_unit: str = None, - payment_type: str = None, - tag: List[CreateResourceGroupMachineGroupRequestTag] = None, + algorithm_name: str = None, + algorithm_provider: str = None, + algorithm_spec: AlgorithmSpec = None, + algorithm_version: str = None, + code_dir: Location = None, + compute_resource: CreateTrainingJobRequestComputeResource = None, + environments: Dict[str, str] = None, + experiment_config: CreateTrainingJobRequestExperimentConfig = None, + hyper_parameters: List[CreateTrainingJobRequestHyperParameters] = None, + input_channels: List[CreateTrainingJobRequestInputChannels] = None, + labels: List[CreateTrainingJobRequestLabels] = None, + output_channels: List[CreateTrainingJobRequestOutputChannels] = None, + python_requirements: List[str] = None, + role_arn: str = None, + scheduler: CreateTrainingJobRequestScheduler = None, + settings: CreateTrainingJobRequestSettings = None, + training_job_description: str = None, + training_job_name: str = None, + user_vpc: CreateTrainingJobRequestUserVpc = None, + workspace_id: str = None, + resource_type: str = None, ): - self.ecs_count = ecs_count - self.ecs_spec = ecs_spec - self.name = name - self.payment_duration = payment_duration - self.payment_duration_unit = payment_duration_unit - self.payment_type = payment_type - self.tag = tag + self.algorithm_name = algorithm_name + self.algorithm_provider = algorithm_provider + self.algorithm_spec = algorithm_spec + self.algorithm_version = algorithm_version + self.code_dir = code_dir + self.compute_resource = compute_resource + self.environments = environments + self.experiment_config = experiment_config + self.hyper_parameters = hyper_parameters + self.input_channels = input_channels + self.labels = labels + self.output_channels = output_channels + self.python_requirements = python_requirements + self.role_arn = role_arn + self.scheduler = scheduler + self.settings = settings + self.training_job_description = training_job_description + # This parameter is required. + self.training_job_name = training_job_name + self.user_vpc = user_vpc + # This parameter is required. + self.workspace_id = workspace_id + self.resource_type = resource_type def validate(self): - if self.tag: - for k in self.tag: + if self.algorithm_spec: + self.algorithm_spec.validate() + if self.code_dir: + self.code_dir.validate() + if self.compute_resource: + self.compute_resource.validate() + if self.experiment_config: + self.experiment_config.validate() + if self.hyper_parameters: + for k in self.hyper_parameters: if k: k.validate() + if self.input_channels: + for k in self.input_channels: + if k: + k.validate() + if self.labels: + for k in self.labels: + if k: + k.validate() + if self.output_channels: + for k in self.output_channels: + if k: + k.validate() + if self.scheduler: + self.scheduler.validate() + if self.settings: + self.settings.validate() + if self.user_vpc: + self.user_vpc.validate() def to_map(self): _map = super().to_map() @@ -5785,85 +6189,163 @@ def to_map(self): return _map result = dict() - if self.ecs_count is not None: - result['EcsCount'] = self.ecs_count - if self.ecs_spec is not None: - result['EcsSpec'] = self.ecs_spec - if self.name is not None: - result['Name'] = self.name - if self.payment_duration is not None: - result['PaymentDuration'] = self.payment_duration - if self.payment_duration_unit is not None: - result['PaymentDurationUnit'] = self.payment_duration_unit - if self.payment_type is not None: - result['PaymentType'] = self.payment_type - result['Tag'] = [] - if self.tag is not None: - for k in self.tag: - result['Tag'].append(k.to_map() if k else None) - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('EcsCount') is not None: - self.ecs_count = m.get('EcsCount') - if m.get('EcsSpec') is not None: - self.ecs_spec = m.get('EcsSpec') - if m.get('Name') is not None: - self.name = m.get('Name') - if m.get('PaymentDuration') is not None: - self.payment_duration = m.get('PaymentDuration') - if m.get('PaymentDurationUnit') is not None: - self.payment_duration_unit = m.get('PaymentDurationUnit') - if m.get('PaymentType') is not None: - self.payment_type = m.get('PaymentType') - self.tag = [] - if m.get('Tag') is not None: - for k in m.get('Tag'): - temp_model = CreateResourceGroupMachineGroupRequestTag() - self.tag.append(temp_model.from_map(k)) - return self - - -class CreateResourceGroupMachineGroupResponseBody(TeaModel): - def __init__( - self, - machine_group_id: str = None, - request_id: str = None, - ): - self.machine_group_id = machine_group_id - self.request_id = request_id - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.machine_group_id is not None: - result['MachineGroupID'] = self.machine_group_id - if self.request_id is not None: - result['RequestId'] = self.request_id + if self.algorithm_name is not None: + result['AlgorithmName'] = self.algorithm_name + if self.algorithm_provider is not None: + result['AlgorithmProvider'] = self.algorithm_provider + if self.algorithm_spec is not None: + result['AlgorithmSpec'] = self.algorithm_spec.to_map() + if self.algorithm_version is not None: + result['AlgorithmVersion'] = self.algorithm_version + if self.code_dir is not None: + result['CodeDir'] = self.code_dir.to_map() + if self.compute_resource is not None: + result['ComputeResource'] = self.compute_resource.to_map() + if self.environments is not None: + result['Environments'] = self.environments + if self.experiment_config is not None: + result['ExperimentConfig'] = self.experiment_config.to_map() + result['HyperParameters'] = [] + if self.hyper_parameters is not None: + for k in self.hyper_parameters: + result['HyperParameters'].append(k.to_map() if k else None) + result['InputChannels'] = [] + if self.input_channels is not None: + for k in self.input_channels: + result['InputChannels'].append(k.to_map() if k else None) + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + result['OutputChannels'] = [] + if self.output_channels is not None: + for k in self.output_channels: + result['OutputChannels'].append(k.to_map() if k else None) + if self.python_requirements is not None: + result['PythonRequirements'] = self.python_requirements + if self.role_arn is not None: + result['RoleArn'] = self.role_arn + if self.scheduler is not None: + result['Scheduler'] = self.scheduler.to_map() + if self.settings is not None: + result['Settings'] = self.settings.to_map() + if self.training_job_description is not None: + result['TrainingJobDescription'] = self.training_job_description + if self.training_job_name is not None: + result['TrainingJobName'] = self.training_job_name + if self.user_vpc is not None: + result['UserVpc'] = self.user_vpc.to_map() + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id + if self.resource_type is not None: + result['ResourceType'] = self.resource_type return result def from_map(self, m: dict = None): m = m or dict() - if m.get('MachineGroupID') is not None: - self.machine_group_id = m.get('MachineGroupID') - if m.get('RequestId') is not None: + if m.get('AlgorithmName') is not None: + self.algorithm_name = m.get('AlgorithmName') + if m.get('AlgorithmProvider') is not None: + self.algorithm_provider = m.get('AlgorithmProvider') + if m.get('AlgorithmSpec') is not None: + temp_model = AlgorithmSpec() + self.algorithm_spec = temp_model.from_map(m['AlgorithmSpec']) + if m.get('AlgorithmVersion') is not None: + self.algorithm_version = m.get('AlgorithmVersion') + if m.get('CodeDir') is not None: + temp_model = Location() + self.code_dir = temp_model.from_map(m['CodeDir']) + if m.get('ComputeResource') is not None: + temp_model = CreateTrainingJobRequestComputeResource() + self.compute_resource = temp_model.from_map(m['ComputeResource']) + if m.get('Environments') is not None: + self.environments = m.get('Environments') + if m.get('ExperimentConfig') is not None: + temp_model = CreateTrainingJobRequestExperimentConfig() + self.experiment_config = temp_model.from_map(m['ExperimentConfig']) + self.hyper_parameters = [] + if m.get('HyperParameters') is not None: + for k in m.get('HyperParameters'): + temp_model = CreateTrainingJobRequestHyperParameters() + self.hyper_parameters.append(temp_model.from_map(k)) + self.input_channels = [] + if m.get('InputChannels') is not None: + for k in m.get('InputChannels'): + temp_model = CreateTrainingJobRequestInputChannels() + self.input_channels.append(temp_model.from_map(k)) + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = CreateTrainingJobRequestLabels() + self.labels.append(temp_model.from_map(k)) + self.output_channels = [] + if m.get('OutputChannels') is not None: + for k in m.get('OutputChannels'): + temp_model = CreateTrainingJobRequestOutputChannels() + self.output_channels.append(temp_model.from_map(k)) + if m.get('PythonRequirements') is not None: + self.python_requirements = m.get('PythonRequirements') + if m.get('RoleArn') is not None: + self.role_arn = m.get('RoleArn') + if m.get('Scheduler') is not None: + temp_model = CreateTrainingJobRequestScheduler() + self.scheduler = temp_model.from_map(m['Scheduler']) + if m.get('Settings') is not None: + temp_model = CreateTrainingJobRequestSettings() + self.settings = temp_model.from_map(m['Settings']) + if m.get('TrainingJobDescription') is not None: + self.training_job_description = m.get('TrainingJobDescription') + if m.get('TrainingJobName') is not None: + self.training_job_name = m.get('TrainingJobName') + if m.get('UserVpc') is not None: + temp_model = CreateTrainingJobRequestUserVpc() + self.user_vpc = temp_model.from_map(m['UserVpc']) + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') + if m.get('ResourceType') is not None: + self.resource_type = m.get('ResourceType') + return self + + +class CreateTrainingJobResponseBody(TeaModel): + def __init__( + self, + request_id: str = None, + training_job_id: str = None, + ): + self.request_id = request_id + self.training_job_id = training_job_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.training_job_id is not None: + result['TrainingJobId'] = self.training_job_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('RequestId') is not None: self.request_id = m.get('RequestId') + if m.get('TrainingJobId') is not None: + self.training_job_id = m.get('TrainingJobId') return self -class CreateResourceGroupMachineGroupResponse(TeaModel): +class CreateTrainingJobResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: CreateResourceGroupMachineGroupResponseBody = None, + body: CreateTrainingJobResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -5894,46 +6376,17 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = CreateResourceGroupMachineGroupResponseBody() + temp_model = CreateTrainingJobResponseBody() self.body = temp_model.from_map(m['body']) return self -class CreateServiceIdentityRoleRequest(TeaModel): - def __init__( - self, - role_name: str = None, - ): - self.role_name = role_name - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.role_name is not None: - result['RoleName'] = self.role_name - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('RoleName') is not None: - self.role_name = m.get('RoleName') - return self - - -class CreateServiceIdentityRoleResponseBody(TeaModel): +class DeleteAlgorithmResponseBody(TeaModel): def __init__( self, request_id: str = None, - role_name: str = None, ): self.request_id = request_id - self.role_name = role_name def validate(self): pass @@ -5945,26 +6398,22 @@ def to_map(self): result = dict() if self.request_id is not None: - result['RequestId'] = self.request_id - if self.role_name is not None: - result['RoleName'] = self.role_name + result['requestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - if m.get('RoleName') is not None: - self.role_name = m.get('RoleName') + if m.get('requestId') is not None: + self.request_id = m.get('requestId') return self -class CreateServiceIdentityRoleResponse(TeaModel): +class DeleteAlgorithmResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: CreateServiceIdentityRoleResponseBody = None, + body: DeleteAlgorithmResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -5995,25 +6444,17 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = CreateServiceIdentityRoleResponseBody() + temp_model = DeleteAlgorithmResponseBody() self.body = temp_model.from_map(m['body']) return self -class CreateTrainingJobRequestComputeResourceInstanceSpec(TeaModel): +class DeleteAlgorithmVersionResponseBody(TeaModel): def __init__( self, - cpu: str = None, - gpu: str = None, - gputype: str = None, - memory: str = None, - shared_memory: str = None, + request_id: str = None, ): - self.cpu = cpu - self.gpu = gpu - self.gputype = gputype - self.memory = memory - self.shared_memory = shared_memory + self.request_id = request_id def validate(self): pass @@ -6024,51 +6465,31 @@ def to_map(self): return _map result = dict() - if self.cpu is not None: - result['CPU'] = self.cpu - if self.gpu is not None: - result['GPU'] = self.gpu - if self.gputype is not None: - result['GPUType'] = self.gputype - if self.memory is not None: - result['Memory'] = self.memory - if self.shared_memory is not None: - result['SharedMemory'] = self.shared_memory + if self.request_id is not None: + result['RequestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('CPU') is not None: - self.cpu = m.get('CPU') - if m.get('GPU') is not None: - self.gpu = m.get('GPU') - if m.get('GPUType') is not None: - self.gputype = m.get('GPUType') - if m.get('Memory') is not None: - self.memory = m.get('Memory') - if m.get('SharedMemory') is not None: - self.shared_memory = m.get('SharedMemory') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') return self -class CreateTrainingJobRequestComputeResource(TeaModel): +class DeleteAlgorithmVersionResponse(TeaModel): def __init__( self, - ecs_count: int = None, - ecs_spec: str = None, - instance_count: int = None, - instance_spec: CreateTrainingJobRequestComputeResourceInstanceSpec = None, - resource_id: str = None, + headers: Dict[str, str] = None, + status_code: int = None, + body: DeleteAlgorithmVersionResponseBody = None, ): - self.ecs_count = ecs_count - self.ecs_spec = ecs_spec - self.instance_count = instance_count - self.instance_spec = instance_spec - self.resource_id = resource_id + self.headers = headers + self.status_code = status_code + self.body = body def validate(self): - if self.instance_spec: - self.instance_spec.validate() + if self.body: + self.body.validate() def to_map(self): _map = super().to_map() @@ -6076,40 +6497,32 @@ def to_map(self): return _map result = dict() - if self.ecs_count is not None: - result['EcsCount'] = self.ecs_count - if self.ecs_spec is not None: - result['EcsSpec'] = self.ecs_spec - if self.instance_count is not None: - result['InstanceCount'] = self.instance_count - if self.instance_spec is not None: - result['InstanceSpec'] = self.instance_spec.to_map() - if self.resource_id is not None: - result['ResourceId'] = self.resource_id + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() return result def from_map(self, m: dict = None): m = m or dict() - if m.get('EcsCount') is not None: - self.ecs_count = m.get('EcsCount') - if m.get('EcsSpec') is not None: - self.ecs_spec = m.get('EcsSpec') - if m.get('InstanceCount') is not None: - self.instance_count = m.get('InstanceCount') - if m.get('InstanceSpec') is not None: - temp_model = CreateTrainingJobRequestComputeResourceInstanceSpec() - self.instance_spec = temp_model.from_map(m['InstanceSpec']) - if m.get('ResourceId') is not None: - self.resource_id = m.get('ResourceId') + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = DeleteAlgorithmVersionResponseBody() + self.body = temp_model.from_map(m['body']) return self -class CreateTrainingJobRequestExperimentConfig(TeaModel): +class DeleteComponentResponseBody(TeaModel): def __init__( self, - experiment_id: str = None, + request_id: str = None, ): - self.experiment_id = experiment_id + self.request_id = request_id def validate(self): pass @@ -6120,28 +6533,31 @@ def to_map(self): return _map result = dict() - if self.experiment_id is not None: - result['ExperimentId'] = self.experiment_id + if self.request_id is not None: + result['RequestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ExperimentId') is not None: - self.experiment_id = m.get('ExperimentId') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') return self -class CreateTrainingJobRequestHyperParameters(TeaModel): +class DeleteComponentResponse(TeaModel): def __init__( self, - name: str = None, - value: str = None, + headers: Dict[str, str] = None, + status_code: int = None, + body: DeleteComponentResponseBody = None, ): - self.name = name - self.value = value + self.headers = headers + self.status_code = status_code + self.body = body def validate(self): - pass + if self.body: + self.body.validate() def to_map(self): _map = super().to_map() @@ -6149,31 +6565,32 @@ def to_map(self): return _map result = dict() - if self.name is not None: - result['Name'] = self.name - if self.value is not None: - result['Value'] = self.value + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Name') is not None: - self.name = m.get('Name') - if m.get('Value') is not None: - self.value = m.get('Value') + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = DeleteComponentResponseBody() + self.body = temp_model.from_map(m['body']) return self -class CreateTrainingJobRequestInputChannels(TeaModel): +class DeleteComponentVersionResponseBody(TeaModel): def __init__( self, - dataset_id: str = None, - input_uri: str = None, - name: str = None, + request_id: str = None, ): - self.dataset_id = dataset_id - self.input_uri = input_uri - self.name = name + self.request_id = request_id def validate(self): pass @@ -6184,36 +6601,31 @@ def to_map(self): return _map result = dict() - if self.dataset_id is not None: - result['DatasetId'] = self.dataset_id - if self.input_uri is not None: - result['InputUri'] = self.input_uri - if self.name is not None: - result['Name'] = self.name + if self.request_id is not None: + result['RequestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('DatasetId') is not None: - self.dataset_id = m.get('DatasetId') - if m.get('InputUri') is not None: - self.input_uri = m.get('InputUri') - if m.get('Name') is not None: - self.name = m.get('Name') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') return self -class CreateTrainingJobRequestLabels(TeaModel): +class DeleteComponentVersionResponse(TeaModel): def __init__( self, - key: str = None, - value: str = None, + headers: Dict[str, str] = None, + status_code: int = None, + body: DeleteComponentVersionResponseBody = None, ): - self.key = key - self.value = value + self.headers = headers + self.status_code = status_code + self.body = body def validate(self): - pass + if self.body: + self.body.validate() def to_map(self): _map = super().to_map() @@ -6221,31 +6633,32 @@ def to_map(self): return _map result = dict() - if self.key is not None: - result['Key'] = self.key - if self.value is not None: - result['Value'] = self.value + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Key') is not None: - self.key = m.get('Key') - if m.get('Value') is not None: - self.value = m.get('Value') + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = DeleteComponentVersionResponseBody() + self.body = temp_model.from_map(m['body']) return self -class CreateTrainingJobRequestOutputChannels(TeaModel): +class DeleteComponentVersionSnapshotResponseBody(TeaModel): def __init__( self, - dataset_id: str = None, - name: str = None, - output_uri: str = None, + request_id: str = None, ): - self.dataset_id = dataset_id - self.name = name - self.output_uri = output_uri + self.request_id = request_id def validate(self): pass @@ -6256,34 +6669,31 @@ def to_map(self): return _map result = dict() - if self.dataset_id is not None: - result['DatasetId'] = self.dataset_id - if self.name is not None: - result['Name'] = self.name - if self.output_uri is not None: - result['OutputUri'] = self.output_uri + if self.request_id is not None: + result['RequestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('DatasetId') is not None: - self.dataset_id = m.get('DatasetId') - if m.get('Name') is not None: - self.name = m.get('Name') - if m.get('OutputUri') is not None: - self.output_uri = m.get('OutputUri') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') return self -class CreateTrainingJobRequestScheduler(TeaModel): +class DeleteComponentVersionSnapshotResponse(TeaModel): def __init__( self, - max_running_time_in_seconds: int = None, + headers: Dict[str, str] = None, + status_code: int = None, + body: DeleteComponentVersionSnapshotResponseBody = None, ): - self.max_running_time_in_seconds = max_running_time_in_seconds + self.headers = headers + self.status_code = status_code + self.body = body def validate(self): - pass + if self.body: + self.body.validate() def to_map(self): _map = super().to_map() @@ -6291,29 +6701,34 @@ def to_map(self): return _map result = dict() - if self.max_running_time_in_seconds is not None: - result['MaxRunningTimeInSeconds'] = self.max_running_time_in_seconds + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() return result def from_map(self, m: dict = None): m = m or dict() - if m.get('MaxRunningTimeInSeconds') is not None: - self.max_running_time_in_seconds = m.get('MaxRunningTimeInSeconds') + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = DeleteComponentVersionSnapshotResponseBody() + self.body = temp_model.from_map(m['body']) return self -class CreateTrainingJobRequestSettings(TeaModel): +class DeleteMachineGroupResponseBody(TeaModel): def __init__( self, - aimaster_type: str = None, - enable_error_monitoring_in_aimaster: bool = None, - error_monitoring_args: str = None, - priority: int = None, + machine_group_id: str = None, + request_id: str = None, ): - self.aimaster_type = aimaster_type - self.enable_error_monitoring_in_aimaster = enable_error_monitoring_in_aimaster - self.error_monitoring_args = error_monitoring_args - self.priority = priority + self.machine_group_id = machine_group_id + self.request_id = request_id def validate(self): pass @@ -6324,46 +6739,35 @@ def to_map(self): return _map result = dict() - if self.aimaster_type is not None: - result['AIMasterType'] = self.aimaster_type - if self.enable_error_monitoring_in_aimaster is not None: - result['EnableErrorMonitoringInAIMaster'] = self.enable_error_monitoring_in_aimaster - if self.error_monitoring_args is not None: - result['ErrorMonitoringArgs'] = self.error_monitoring_args - if self.priority is not None: - result['Priority'] = self.priority + if self.machine_group_id is not None: + result['MachineGroupID'] = self.machine_group_id + if self.request_id is not None: + result['RequestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('AIMasterType') is not None: - self.aimaster_type = m.get('AIMasterType') - if m.get('EnableErrorMonitoringInAIMaster') is not None: - self.enable_error_monitoring_in_aimaster = m.get('EnableErrorMonitoringInAIMaster') - if m.get('ErrorMonitoringArgs') is not None: - self.error_monitoring_args = m.get('ErrorMonitoringArgs') - if m.get('Priority') is not None: - self.priority = m.get('Priority') + if m.get('MachineGroupID') is not None: + self.machine_group_id = m.get('MachineGroupID') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') return self -class CreateTrainingJobRequestUserVpc(TeaModel): +class DeleteMachineGroupResponse(TeaModel): def __init__( self, - default_route: str = None, - extended_cidrs: List[str] = None, - security_group_id: str = None, - switch_id: str = None, - vpc_id: str = None, + headers: Dict[str, str] = None, + status_code: int = None, + body: DeleteMachineGroupResponseBody = None, ): - self.default_route = default_route - self.extended_cidrs = extended_cidrs - self.security_group_id = security_group_id - self.switch_id = switch_id - self.vpc_id = vpc_id + self.headers = headers + self.status_code = status_code + self.body = body def validate(self): - pass + if self.body: + self.body.validate() def to_map(self): _map = super().to_map() @@ -6371,238 +6775,35 @@ def to_map(self): return _map result = dict() - if self.default_route is not None: - result['DefaultRoute'] = self.default_route - if self.extended_cidrs is not None: - result['ExtendedCIDRs'] = self.extended_cidrs - if self.security_group_id is not None: - result['SecurityGroupId'] = self.security_group_id - if self.switch_id is not None: - result['SwitchId'] = self.switch_id - if self.vpc_id is not None: - result['VpcId'] = self.vpc_id + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() return result def from_map(self, m: dict = None): m = m or dict() - if m.get('DefaultRoute') is not None: - self.default_route = m.get('DefaultRoute') - if m.get('ExtendedCIDRs') is not None: - self.extended_cidrs = m.get('ExtendedCIDRs') - if m.get('SecurityGroupId') is not None: - self.security_group_id = m.get('SecurityGroupId') - if m.get('SwitchId') is not None: - self.switch_id = m.get('SwitchId') - if m.get('VpcId') is not None: - self.vpc_id = m.get('VpcId') + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = DeleteMachineGroupResponseBody() + self.body = temp_model.from_map(m['body']) return self -class CreateTrainingJobRequest(TeaModel): - def __init__( - self, - algorithm_name: str = None, - algorithm_provider: str = None, - algorithm_spec: AlgorithmSpec = None, - algorithm_version: str = None, - code_dir: Location = None, - compute_resource: CreateTrainingJobRequestComputeResource = None, - environments: Dict[str, str] = None, - experiment_config: CreateTrainingJobRequestExperimentConfig = None, - hyper_parameters: List[CreateTrainingJobRequestHyperParameters] = None, - input_channels: List[CreateTrainingJobRequestInputChannels] = None, - labels: List[CreateTrainingJobRequestLabels] = None, - output_channels: List[CreateTrainingJobRequestOutputChannels] = None, - python_requirements: List[str] = None, - role_arn: str = None, - scheduler: CreateTrainingJobRequestScheduler = None, - settings: CreateTrainingJobRequestSettings = None, - training_job_description: str = None, - training_job_name: str = None, - user_vpc: CreateTrainingJobRequestUserVpc = None, - workspace_id: str = None, - ): - self.algorithm_name = algorithm_name - self.algorithm_provider = algorithm_provider - self.algorithm_spec = algorithm_spec - self.algorithm_version = algorithm_version - self.code_dir = code_dir - self.compute_resource = compute_resource - self.environments = environments - self.experiment_config = experiment_config - self.hyper_parameters = hyper_parameters - self.input_channels = input_channels - self.labels = labels - self.output_channels = output_channels - self.python_requirements = python_requirements - self.role_arn = role_arn - self.scheduler = scheduler - self.settings = settings - self.training_job_description = training_job_description - self.training_job_name = training_job_name - self.user_vpc = user_vpc - self.workspace_id = workspace_id - - def validate(self): - if self.algorithm_spec: - self.algorithm_spec.validate() - if self.code_dir: - self.code_dir.validate() - if self.compute_resource: - self.compute_resource.validate() - if self.experiment_config: - self.experiment_config.validate() - if self.hyper_parameters: - for k in self.hyper_parameters: - if k: - k.validate() - if self.input_channels: - for k in self.input_channels: - if k: - k.validate() - if self.labels: - for k in self.labels: - if k: - k.validate() - if self.output_channels: - for k in self.output_channels: - if k: - k.validate() - if self.scheduler: - self.scheduler.validate() - if self.settings: - self.settings.validate() - if self.user_vpc: - self.user_vpc.validate() - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.algorithm_name is not None: - result['AlgorithmName'] = self.algorithm_name - if self.algorithm_provider is not None: - result['AlgorithmProvider'] = self.algorithm_provider - if self.algorithm_spec is not None: - result['AlgorithmSpec'] = self.algorithm_spec.to_map() - if self.algorithm_version is not None: - result['AlgorithmVersion'] = self.algorithm_version - if self.code_dir is not None: - result['CodeDir'] = self.code_dir.to_map() - if self.compute_resource is not None: - result['ComputeResource'] = self.compute_resource.to_map() - if self.environments is not None: - result['Environments'] = self.environments - if self.experiment_config is not None: - result['ExperimentConfig'] = self.experiment_config.to_map() - result['HyperParameters'] = [] - if self.hyper_parameters is not None: - for k in self.hyper_parameters: - result['HyperParameters'].append(k.to_map() if k else None) - result['InputChannels'] = [] - if self.input_channels is not None: - for k in self.input_channels: - result['InputChannels'].append(k.to_map() if k else None) - result['Labels'] = [] - if self.labels is not None: - for k in self.labels: - result['Labels'].append(k.to_map() if k else None) - result['OutputChannels'] = [] - if self.output_channels is not None: - for k in self.output_channels: - result['OutputChannels'].append(k.to_map() if k else None) - if self.python_requirements is not None: - result['PythonRequirements'] = self.python_requirements - if self.role_arn is not None: - result['RoleArn'] = self.role_arn - if self.scheduler is not None: - result['Scheduler'] = self.scheduler.to_map() - if self.settings is not None: - result['Settings'] = self.settings.to_map() - if self.training_job_description is not None: - result['TrainingJobDescription'] = self.training_job_description - if self.training_job_name is not None: - result['TrainingJobName'] = self.training_job_name - if self.user_vpc is not None: - result['UserVpc'] = self.user_vpc.to_map() - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('AlgorithmName') is not None: - self.algorithm_name = m.get('AlgorithmName') - if m.get('AlgorithmProvider') is not None: - self.algorithm_provider = m.get('AlgorithmProvider') - if m.get('AlgorithmSpec') is not None: - temp_model = AlgorithmSpec() - self.algorithm_spec = temp_model.from_map(m['AlgorithmSpec']) - if m.get('AlgorithmVersion') is not None: - self.algorithm_version = m.get('AlgorithmVersion') - if m.get('CodeDir') is not None: - temp_model = Location() - self.code_dir = temp_model.from_map(m['CodeDir']) - if m.get('ComputeResource') is not None: - temp_model = CreateTrainingJobRequestComputeResource() - self.compute_resource = temp_model.from_map(m['ComputeResource']) - if m.get('Environments') is not None: - self.environments = m.get('Environments') - if m.get('ExperimentConfig') is not None: - temp_model = CreateTrainingJobRequestExperimentConfig() - self.experiment_config = temp_model.from_map(m['ExperimentConfig']) - self.hyper_parameters = [] - if m.get('HyperParameters') is not None: - for k in m.get('HyperParameters'): - temp_model = CreateTrainingJobRequestHyperParameters() - self.hyper_parameters.append(temp_model.from_map(k)) - self.input_channels = [] - if m.get('InputChannels') is not None: - for k in m.get('InputChannels'): - temp_model = CreateTrainingJobRequestInputChannels() - self.input_channels.append(temp_model.from_map(k)) - self.labels = [] - if m.get('Labels') is not None: - for k in m.get('Labels'): - temp_model = CreateTrainingJobRequestLabels() - self.labels.append(temp_model.from_map(k)) - self.output_channels = [] - if m.get('OutputChannels') is not None: - for k in m.get('OutputChannels'): - temp_model = CreateTrainingJobRequestOutputChannels() - self.output_channels.append(temp_model.from_map(k)) - if m.get('PythonRequirements') is not None: - self.python_requirements = m.get('PythonRequirements') - if m.get('RoleArn') is not None: - self.role_arn = m.get('RoleArn') - if m.get('Scheduler') is not None: - temp_model = CreateTrainingJobRequestScheduler() - self.scheduler = temp_model.from_map(m['Scheduler']) - if m.get('Settings') is not None: - temp_model = CreateTrainingJobRequestSettings() - self.settings = temp_model.from_map(m['Settings']) - if m.get('TrainingJobDescription') is not None: - self.training_job_description = m.get('TrainingJobDescription') - if m.get('TrainingJobName') is not None: - self.training_job_name = m.get('TrainingJobName') - if m.get('UserVpc') is not None: - temp_model = CreateTrainingJobRequestUserVpc() - self.user_vpc = temp_model.from_map(m['UserVpc']) - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') - return self - - -class CreateTrainingJobResponseBody(TeaModel): +class DeleteQuotaResponseBody(TeaModel): def __init__( self, + quota_id: str = None, request_id: str = None, - training_job_id: str = None, ): + # Quota Id + self.quota_id = quota_id self.request_id = request_id - self.training_job_id = training_job_id def validate(self): pass @@ -6613,27 +6814,27 @@ def to_map(self): return _map result = dict() + if self.quota_id is not None: + result['QuotaId'] = self.quota_id if self.request_id is not None: result['RequestId'] = self.request_id - if self.training_job_id is not None: - result['TrainingJobId'] = self.training_job_id return result def from_map(self, m: dict = None): m = m or dict() + if m.get('QuotaId') is not None: + self.quota_id = m.get('QuotaId') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('TrainingJobId') is not None: - self.training_job_id = m.get('TrainingJobId') return self -class CreateTrainingJobResponse(TeaModel): +class DeleteQuotaResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: CreateTrainingJobResponseBody = None, + body: DeleteQuotaResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -6664,16 +6865,45 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = CreateTrainingJobResponseBody() + temp_model = DeleteQuotaResponseBody() self.body = temp_model.from_map(m['body']) return self -class DeleteAlgorithmResponseBody(TeaModel): +class DeleteQuotaLabelsRequest(TeaModel): + def __init__( + self, + keys: str = None, + ): + self.keys = keys + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.keys is not None: + result['Keys'] = self.keys + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Keys') is not None: + self.keys = m.get('Keys') + return self + + +class DeleteQuotaLabelsResponseBody(TeaModel): def __init__( self, + quota_id: str = None, request_id: str = None, ): + self.quota_id = quota_id self.request_id = request_id def validate(self): @@ -6685,23 +6915,27 @@ def to_map(self): return _map result = dict() + if self.quota_id is not None: + result['QuotaId'] = self.quota_id if self.request_id is not None: - result['requestId'] = self.request_id + result['RequestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('requestId') is not None: - self.request_id = m.get('requestId') + if m.get('QuotaId') is not None: + self.quota_id = m.get('QuotaId') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') return self -class DeleteAlgorithmResponse(TeaModel): +class DeleteQuotaLabelsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: DeleteAlgorithmResponseBody = None, + body: DeleteQuotaLabelsResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -6732,17 +6966,19 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = DeleteAlgorithmResponseBody() + temp_model = DeleteQuotaLabelsResponseBody() self.body = temp_model.from_map(m['body']) return self -class DeleteAlgorithmVersionResponseBody(TeaModel): +class DeleteResourceGroupResponseBody(TeaModel): def __init__( self, request_id: str = None, + resource_group_id: str = None, ): self.request_id = request_id + self.resource_group_id = resource_group_id def validate(self): pass @@ -6755,21 +6991,25 @@ def to_map(self): result = dict() if self.request_id is not None: result['RequestId'] = self.request_id + if self.resource_group_id is not None: + result['ResourceGroupID'] = self.resource_group_id return result def from_map(self, m: dict = None): m = m or dict() if m.get('RequestId') is not None: self.request_id = m.get('RequestId') + if m.get('ResourceGroupID') is not None: + self.resource_group_id = m.get('ResourceGroupID') return self -class DeleteAlgorithmVersionResponse(TeaModel): +class DeleteResourceGroupResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: DeleteAlgorithmVersionResponseBody = None, + body: DeleteResourceGroupResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -6800,16 +7040,18 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = DeleteAlgorithmVersionResponseBody() + temp_model = DeleteResourceGroupResponseBody() self.body = temp_model.from_map(m['body']) return self -class DeleteComponentResponseBody(TeaModel): +class DeleteResourceGroupMachineGroupResponseBody(TeaModel): def __init__( self, + machine_group_id: str = None, request_id: str = None, ): + self.machine_group_id = machine_group_id self.request_id = request_id def validate(self): @@ -6821,23 +7063,27 @@ def to_map(self): return _map result = dict() + if self.machine_group_id is not None: + result['MachineGroupID'] = self.machine_group_id if self.request_id is not None: result['RequestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() + if m.get('MachineGroupID') is not None: + self.machine_group_id = m.get('MachineGroupID') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') return self -class DeleteComponentResponse(TeaModel): +class DeleteResourceGroupMachineGroupResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: DeleteComponentResponseBody = None, + body: DeleteResourceGroupMachineGroupResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -6868,12 +7114,12 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = DeleteComponentResponseBody() + temp_model = DeleteResourceGroupMachineGroupResponseBody() self.body = temp_model.from_map(m['body']) return self -class DeleteComponentVersionResponseBody(TeaModel): +class DeleteTrainingJobResponseBody(TeaModel): def __init__( self, request_id: str = None, @@ -6900,12 +7146,12 @@ def from_map(self, m: dict = None): return self -class DeleteComponentVersionResponse(TeaModel): +class DeleteTrainingJobResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: DeleteComponentVersionResponseBody = None, + body: DeleteTrainingJobResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -6936,12 +7182,40 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = DeleteComponentVersionResponseBody() + temp_model = DeleteTrainingJobResponseBody() self.body = temp_model.from_map(m['body']) return self -class DeleteComponentVersionSnapshotResponseBody(TeaModel): +class DeleteTrainingJobLabelsRequest(TeaModel): + def __init__( + self, + keys: str = None, + ): + # This parameter is required. + self.keys = keys + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.keys is not None: + result['Keys'] = self.keys + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Keys') is not None: + self.keys = m.get('Keys') + return self + + +class DeleteTrainingJobLabelsResponseBody(TeaModel): def __init__( self, request_id: str = None, @@ -6968,12 +7242,12 @@ def from_map(self, m: dict = None): return self -class DeleteComponentVersionSnapshotResponse(TeaModel): +class DeleteTrainingJobLabelsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: DeleteComponentVersionSnapshotResponseBody = None, + body: DeleteTrainingJobLabelsResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -7004,16 +7278,22 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = DeleteComponentVersionSnapshotResponseBody() + temp_model = DeleteTrainingJobLabelsResponseBody() self.body = temp_model.from_map(m['body']) return self -class DeleteLLMProjectResponseBody(TeaModel): +class GetAI4DDefaultBucketResponseBody(TeaModel): def __init__( self, + extranet_endpoint: str = None, + intranet_endpoint: str = None, + name: str = None, request_id: str = None, ): + self.extranet_endpoint = extranet_endpoint + self.intranet_endpoint = intranet_endpoint + self.name = name self.request_id = request_id def validate(self): @@ -7025,23 +7305,35 @@ def to_map(self): return _map result = dict() + if self.extranet_endpoint is not None: + result['ExtranetEndpoint'] = self.extranet_endpoint + if self.intranet_endpoint is not None: + result['IntranetEndpoint'] = self.intranet_endpoint + if self.name is not None: + result['Name'] = self.name if self.request_id is not None: result['RequestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() + if m.get('ExtranetEndpoint') is not None: + self.extranet_endpoint = m.get('ExtranetEndpoint') + if m.get('IntranetEndpoint') is not None: + self.intranet_endpoint = m.get('IntranetEndpoint') + if m.get('Name') is not None: + self.name = m.get('Name') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') return self -class DeleteLLMProjectResponse(TeaModel): +class GetAI4DDefaultBucketResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: DeleteLLMProjectResponseBody = None, + body: GetAI4DDefaultBucketResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -7072,19 +7364,37 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = DeleteLLMProjectResponseBody() + temp_model = GetAI4DDefaultBucketResponseBody() self.body = temp_model.from_map(m['body']) return self -class DeleteMachineGroupResponseBody(TeaModel): +class GetAlgorithmResponseBody(TeaModel): def __init__( self, - machine_group_id: str = None, + algorithm_description: str = None, + algorithm_id: str = None, + algorithm_name: str = None, + algorithm_provider: str = None, + display_name: str = None, + gmt_create_time: str = None, + gmt_modified_time: str = None, request_id: str = None, + tenant_id: str = None, + user_id: str = None, + workspace_id: str = None, ): - self.machine_group_id = machine_group_id + self.algorithm_description = algorithm_description + self.algorithm_id = algorithm_id + self.algorithm_name = algorithm_name + self.algorithm_provider = algorithm_provider + self.display_name = display_name + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time self.request_id = request_id + self.tenant_id = tenant_id + self.user_id = user_id + self.workspace_id = workspace_id def validate(self): pass @@ -7095,27 +7405,63 @@ def to_map(self): return _map result = dict() - if self.machine_group_id is not None: - result['MachineGroupID'] = self.machine_group_id + if self.algorithm_description is not None: + result['AlgorithmDescription'] = self.algorithm_description + if self.algorithm_id is not None: + result['AlgorithmId'] = self.algorithm_id + if self.algorithm_name is not None: + result['AlgorithmName'] = self.algorithm_name + if self.algorithm_provider is not None: + result['AlgorithmProvider'] = self.algorithm_provider + if self.display_name is not None: + result['DisplayName'] = self.display_name + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time if self.request_id is not None: result['RequestId'] = self.request_id + if self.tenant_id is not None: + result['TenantId'] = self.tenant_id + if self.user_id is not None: + result['UserId'] = self.user_id + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('MachineGroupID') is not None: - self.machine_group_id = m.get('MachineGroupID') + if m.get('AlgorithmDescription') is not None: + self.algorithm_description = m.get('AlgorithmDescription') + if m.get('AlgorithmId') is not None: + self.algorithm_id = m.get('AlgorithmId') + if m.get('AlgorithmName') is not None: + self.algorithm_name = m.get('AlgorithmName') + if m.get('AlgorithmProvider') is not None: + self.algorithm_provider = m.get('AlgorithmProvider') + if m.get('DisplayName') is not None: + self.display_name = m.get('DisplayName') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') + if m.get('TenantId') is not None: + self.tenant_id = m.get('TenantId') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class DeleteMachineGroupResponse(TeaModel): +class GetAlgorithmResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: DeleteMachineGroupResponseBody = None, + body: GetAlgorithmResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -7146,23 +7492,37 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = DeleteMachineGroupResponseBody() + temp_model = GetAlgorithmResponseBody() self.body = temp_model.from_map(m['body']) return self -class DeleteQuotaResponseBody(TeaModel): +class GetAlgorithmVersionResponseBody(TeaModel): def __init__( self, - quota_id: str = None, - request_id: str = None, + algorithm_id: str = None, + algorithm_name: str = None, + algorithm_provider: str = None, + algorithm_spec: AlgorithmSpec = None, + algorithm_version: str = None, + gmt_create_time: str = None, + gmt_modified_time: str = None, + tenant_id: str = None, + user_id: str = None, ): - # Quota Id - self.quota_id = quota_id - self.request_id = request_id + self.algorithm_id = algorithm_id + self.algorithm_name = algorithm_name + self.algorithm_provider = algorithm_provider + self.algorithm_spec = algorithm_spec + self.algorithm_version = algorithm_version + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time + self.tenant_id = tenant_id + self.user_id = user_id def validate(self): - pass + if self.algorithm_spec: + self.algorithm_spec.validate() def to_map(self): _map = super().to_map() @@ -7170,27 +7530,56 @@ def to_map(self): return _map result = dict() - if self.quota_id is not None: - result['QuotaId'] = self.quota_id - if self.request_id is not None: - result['RequestId'] = self.request_id + if self.algorithm_id is not None: + result['AlgorithmId'] = self.algorithm_id + if self.algorithm_name is not None: + result['AlgorithmName'] = self.algorithm_name + if self.algorithm_provider is not None: + result['AlgorithmProvider'] = self.algorithm_provider + if self.algorithm_spec is not None: + result['AlgorithmSpec'] = self.algorithm_spec.to_map() + if self.algorithm_version is not None: + result['AlgorithmVersion'] = self.algorithm_version + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + if self.tenant_id is not None: + result['TenantId'] = self.tenant_id + if self.user_id is not None: + result['UserId'] = self.user_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('QuotaId') is not None: - self.quota_id = m.get('QuotaId') - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') + if m.get('AlgorithmId') is not None: + self.algorithm_id = m.get('AlgorithmId') + if m.get('AlgorithmName') is not None: + self.algorithm_name = m.get('AlgorithmName') + if m.get('AlgorithmProvider') is not None: + self.algorithm_provider = m.get('AlgorithmProvider') + if m.get('AlgorithmSpec') is not None: + temp_model = AlgorithmSpec() + self.algorithm_spec = temp_model.from_map(m['AlgorithmSpec']) + if m.get('AlgorithmVersion') is not None: + self.algorithm_version = m.get('AlgorithmVersion') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + if m.get('TenantId') is not None: + self.tenant_id = m.get('TenantId') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') return self -class DeleteQuotaResponse(TeaModel): +class GetAlgorithmVersionResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: DeleteQuotaResponseBody = None, + body: GetAlgorithmVersionResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -7221,17 +7610,24 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = DeleteQuotaResponseBody() + temp_model = GetAlgorithmVersionResponseBody() self.body = temp_model.from_map(m['body']) return self -class DeleteQuotaLabelsRequest(TeaModel): +class GetComponentResponseBodyVersions(TeaModel): def __init__( self, - keys: str = None, + gmt_create_time: str = None, + snapshot_id: str = None, + status: str = None, + version: str = None, ): - self.keys = keys + # Use the UTC time format: yyyy-MM-ddTHH:mmZ + self.gmt_create_time = gmt_create_time + self.snapshot_id = snapshot_id + self.status = status + self.version = version def validate(self): pass @@ -7242,28 +7638,71 @@ def to_map(self): return _map result = dict() - if self.keys is not None: - result['Keys'] = self.keys + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.snapshot_id is not None: + result['SnapshotId'] = self.snapshot_id + if self.status is not None: + result['Status'] = self.status + if self.version is not None: + result['Version'] = self.version return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Keys') is not None: - self.keys = m.get('Keys') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('SnapshotId') is not None: + self.snapshot_id = m.get('SnapshotId') + if m.get('Status') is not None: + self.status = m.get('Status') + if m.get('Version') is not None: + self.version = m.get('Version') return self -class DeleteQuotaLabelsResponseBody(TeaModel): +class GetComponentResponseBody(TeaModel): def __init__( self, - quota_id: str = None, + component_id: str = None, + description: str = None, + display_name: str = None, + gmt_create_time: str = None, + gmt_modified_time: str = None, + labels: List[Label] = None, + name: str = None, + provider: str = None, request_id: str = None, + tenant_id: str = None, + user_id: str = None, + versions: List[GetComponentResponseBodyVersions] = None, + workspace_id: str = None, ): - self.quota_id = quota_id + self.component_id = component_id + self.description = description + self.display_name = display_name + # Use the UTC time format: yyyy-MM-ddTHH:mmZ + self.gmt_create_time = gmt_create_time + # Use the UTC time format: yyyy-MM-ddTHH:mmZ + self.gmt_modified_time = gmt_modified_time + self.labels = labels + self.name = name + self.provider = provider self.request_id = request_id + self.tenant_id = tenant_id + self.user_id = user_id + self.versions = versions + self.workspace_id = workspace_id def validate(self): - pass + if self.labels: + for k in self.labels: + if k: + k.validate() + if self.versions: + for k in self.versions: + if k: + k.validate() def to_map(self): _map = super().to_map() @@ -7271,27 +7710,81 @@ def to_map(self): return _map result = dict() - if self.quota_id is not None: - result['QuotaId'] = self.quota_id + if self.component_id is not None: + result['ComponentId'] = self.component_id + if self.description is not None: + result['Description'] = self.description + if self.display_name is not None: + result['DisplayName'] = self.display_name + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + if self.name is not None: + result['Name'] = self.name + if self.provider is not None: + result['Provider'] = self.provider if self.request_id is not None: result['RequestId'] = self.request_id + if self.tenant_id is not None: + result['TenantId'] = self.tenant_id + if self.user_id is not None: + result['UserId'] = self.user_id + result['Versions'] = [] + if self.versions is not None: + for k in self.versions: + result['Versions'].append(k.to_map() if k else None) + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('QuotaId') is not None: - self.quota_id = m.get('QuotaId') + if m.get('ComponentId') is not None: + self.component_id = m.get('ComponentId') + if m.get('Description') is not None: + self.description = m.get('Description') + if m.get('DisplayName') is not None: + self.display_name = m.get('DisplayName') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = Label() + self.labels.append(temp_model.from_map(k)) + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('Provider') is not None: + self.provider = m.get('Provider') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') + if m.get('TenantId') is not None: + self.tenant_id = m.get('TenantId') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + self.versions = [] + if m.get('Versions') is not None: + for k in m.get('Versions'): + temp_model = GetComponentResponseBodyVersions() + self.versions.append(temp_model.from_map(k)) + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class DeleteQuotaLabelsResponse(TeaModel): +class GetComponentResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: DeleteQuotaLabelsResponseBody = None, + body: GetComponentResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -7322,22 +7815,51 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = DeleteQuotaLabelsResponseBody() + temp_model = GetComponentResponseBody() self.body = temp_model.from_map(m['body']) return self -class DeleteResourceGroupResponseBody(TeaModel): +class GetComponentVersionResponseBody(TeaModel): def __init__( self, + description: str = None, + gmt_create_time: str = None, + gmt_modified_time: str = None, + labels: List[Label] = None, + name: str = None, + provider: str = None, request_id: str = None, - resource_group_id: str = None, + snapshot_id: str = None, + spec: ComponentSpec = None, + tenant_id: str = None, + user_id: str = None, + version: str = None, + workspace_id: str = None, ): + self.description = description + # Use the UTC time format: yyyy-MM-ddTHH:mmZ + self.gmt_create_time = gmt_create_time + # Use the UTC time format: yyyy-MM-ddTHH:mmZ + self.gmt_modified_time = gmt_modified_time + self.labels = labels + self.name = name + self.provider = provider self.request_id = request_id - self.resource_group_id = resource_group_id + self.snapshot_id = snapshot_id + self.spec = spec + self.tenant_id = tenant_id + self.user_id = user_id + self.version = version + self.workspace_id = workspace_id def validate(self): - pass + if self.labels: + for k in self.labels: + if k: + k.validate() + if self.spec: + self.spec.validate() def to_map(self): _map = super().to_map() @@ -7345,27 +7867,77 @@ def to_map(self): return _map result = dict() + if self.description is not None: + result['Description'] = self.description + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + if self.name is not None: + result['Name'] = self.name + if self.provider is not None: + result['Provider'] = self.provider if self.request_id is not None: result['RequestId'] = self.request_id - if self.resource_group_id is not None: - result['ResourceGroupID'] = self.resource_group_id + if self.snapshot_id is not None: + result['SnapshotId'] = self.snapshot_id + if self.spec is not None: + result['Spec'] = self.spec.to_map() + if self.tenant_id is not None: + result['TenantId'] = self.tenant_id + if self.user_id is not None: + result['UserId'] = self.user_id + if self.version is not None: + result['Version'] = self.version + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() + if m.get('Description') is not None: + self.description = m.get('Description') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = Label() + self.labels.append(temp_model.from_map(k)) + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('Provider') is not None: + self.provider = m.get('Provider') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('ResourceGroupID') is not None: - self.resource_group_id = m.get('ResourceGroupID') + if m.get('SnapshotId') is not None: + self.snapshot_id = m.get('SnapshotId') + if m.get('Spec') is not None: + temp_model = ComponentSpec() + self.spec = temp_model.from_map(m['Spec']) + if m.get('TenantId') is not None: + self.tenant_id = m.get('TenantId') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + if m.get('Version') is not None: + self.version = m.get('Version') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class DeleteResourceGroupResponse(TeaModel): +class GetComponentVersionResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: DeleteResourceGroupResponseBody = None, + body: GetComponentVersionResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -7396,19 +7968,37 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = DeleteResourceGroupResponseBody() + temp_model = GetComponentVersionResponseBody() self.body = temp_model.from_map(m['body']) return self -class DeleteResourceGroupMachineGroupResponseBody(TeaModel): +class GetComponentVersionSnapshotResponseBody(TeaModel): def __init__( self, - machine_group_id: str = None, + component_id: str = None, + gmt_create_time: str = None, + gmt_modified_time: str = None, + is_current_version: bool = None, request_id: str = None, + snapshot_id: str = None, + tenant_id: str = None, + user_id: str = None, + version: str = None, + workspace_id: str = None, ): - self.machine_group_id = machine_group_id + self.component_id = component_id + # Use the UTC time format: yyyy-MM-ddTHH:mmZ + self.gmt_create_time = gmt_create_time + # Use the UTC time format: yyyy-MM-ddTHH:mmZ + self.gmt_modified_time = gmt_modified_time + self.is_current_version = is_current_version self.request_id = request_id + self.snapshot_id = snapshot_id + self.tenant_id = tenant_id + self.user_id = user_id + self.version = version + self.workspace_id = workspace_id def validate(self): pass @@ -7419,27 +8009,59 @@ def to_map(self): return _map result = dict() - if self.machine_group_id is not None: - result['MachineGroupID'] = self.machine_group_id + if self.component_id is not None: + result['ComponentId'] = self.component_id + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + if self.is_current_version is not None: + result['IsCurrentVersion'] = self.is_current_version if self.request_id is not None: result['RequestId'] = self.request_id - return result + if self.snapshot_id is not None: + result['SnapshotId'] = self.snapshot_id + if self.tenant_id is not None: + result['TenantId'] = self.tenant_id + if self.user_id is not None: + result['UserId'] = self.user_id + if self.version is not None: + result['Version'] = self.version + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id + return result def from_map(self, m: dict = None): m = m or dict() - if m.get('MachineGroupID') is not None: - self.machine_group_id = m.get('MachineGroupID') + if m.get('ComponentId') is not None: + self.component_id = m.get('ComponentId') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + if m.get('IsCurrentVersion') is not None: + self.is_current_version = m.get('IsCurrentVersion') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') + if m.get('SnapshotId') is not None: + self.snapshot_id = m.get('SnapshotId') + if m.get('TenantId') is not None: + self.tenant_id = m.get('TenantId') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + if m.get('Version') is not None: + self.version = m.get('Version') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class DeleteResourceGroupMachineGroupResponse(TeaModel): +class GetComponentVersionSnapshotResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: DeleteResourceGroupMachineGroupResponseBody = None, + body: GetComponentVersionSnapshotResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -7470,17 +8092,36 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = DeleteResourceGroupMachineGroupResponseBody() + temp_model = GetComponentVersionSnapshotResponseBody() self.body = temp_model.from_map(m['body']) return self -class DeleteTrainingJobResponseBody(TeaModel): +class GetInstanceJobResponseBody(TeaModel): def __init__( self, + creator: str = None, + gmt_create_time: str = None, + instance_id: str = None, + instance_job_id: str = None, + instance_job_type: str = None, + reason_code: str = None, + reason_message: str = None, request_id: str = None, + status: str = None, + workspace_id: str = None, ): + self.creator = creator + # Use the UTC time format: yyyy-MM-ddTHH:mmZ + self.gmt_create_time = gmt_create_time + self.instance_id = instance_id + self.instance_job_id = instance_job_id + self.instance_job_type = instance_job_type + self.reason_code = reason_code + self.reason_message = reason_message self.request_id = request_id + self.status = status + self.workspace_id = workspace_id def validate(self): pass @@ -7491,23 +8132,59 @@ def to_map(self): return _map result = dict() + if self.creator is not None: + result['Creator'] = self.creator + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.instance_id is not None: + result['InstanceId'] = self.instance_id + if self.instance_job_id is not None: + result['InstanceJobId'] = self.instance_job_id + if self.instance_job_type is not None: + result['InstanceJobType'] = self.instance_job_type + if self.reason_code is not None: + result['ReasonCode'] = self.reason_code + if self.reason_message is not None: + result['ReasonMessage'] = self.reason_message if self.request_id is not None: result['RequestId'] = self.request_id + if self.status is not None: + result['Status'] = self.status + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() + if m.get('Creator') is not None: + self.creator = m.get('Creator') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('InstanceId') is not None: + self.instance_id = m.get('InstanceId') + if m.get('InstanceJobId') is not None: + self.instance_job_id = m.get('InstanceJobId') + if m.get('InstanceJobType') is not None: + self.instance_job_type = m.get('InstanceJobType') + if m.get('ReasonCode') is not None: + self.reason_code = m.get('ReasonCode') + if m.get('ReasonMessage') is not None: + self.reason_message = m.get('ReasonMessage') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') + if m.get('Status') is not None: + self.status = m.get('Status') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class DeleteTrainingJobResponse(TeaModel): +class GetInstanceJobResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: DeleteTrainingJobResponseBody = None, + body: GetInstanceJobResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -7538,17 +8215,29 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = DeleteTrainingJobResponseBody() + temp_model = GetInstanceJobResponseBody() self.body = temp_model.from_map(m['body']) return self -class DeleteTrainingJobLabelsRequest(TeaModel): +class GetJobViewMetricsRequest(TeaModel): def __init__( self, - keys: str = None, + end_time: str = None, + page_number: int = None, + page_size: int = None, + sort_by: str = None, + start_time: str = None, + time_step: str = None, + workspace_id: str = None, ): - self.keys = keys + self.end_time = end_time + self.page_number = page_number + self.page_size = page_size + self.sort_by = sort_by + self.start_time = start_time + self.time_step = time_step + self.workspace_id = workspace_id def validate(self): pass @@ -7559,26 +8248,61 @@ def to_map(self): return _map result = dict() - if self.keys is not None: - result['Keys'] = self.keys + if self.end_time is not None: + result['EndTime'] = self.end_time + if self.page_number is not None: + result['PageNumber'] = self.page_number + if self.page_size is not None: + result['PageSize'] = self.page_size + if self.sort_by is not None: + result['SortBy'] = self.sort_by + if self.start_time is not None: + result['StartTime'] = self.start_time + if self.time_step is not None: + result['TimeStep'] = self.time_step + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Keys') is not None: - self.keys = m.get('Keys') + if m.get('EndTime') is not None: + self.end_time = m.get('EndTime') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + if m.get('SortBy') is not None: + self.sort_by = m.get('SortBy') + if m.get('StartTime') is not None: + self.start_time = m.get('StartTime') + if m.get('TimeStep') is not None: + self.time_step = m.get('TimeStep') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class DeleteTrainingJobLabelsResponseBody(TeaModel): +class GetJobViewMetricsResponseBody(TeaModel): def __init__( self, + job_metrics: List[JobViewMetric] = None, request_id: str = None, + summary: JobViewMetric = None, + total: int = None, ): + self.job_metrics = job_metrics self.request_id = request_id + self.summary = summary + self.total = total def validate(self): - pass + if self.job_metrics: + for k in self.job_metrics: + if k: + k.validate() + if self.summary: + self.summary.validate() def to_map(self): _map = super().to_map() @@ -7586,23 +8310,41 @@ def to_map(self): return _map result = dict() + result['JobMetrics'] = [] + if self.job_metrics is not None: + for k in self.job_metrics: + result['JobMetrics'].append(k.to_map() if k else None) if self.request_id is not None: result['RequestId'] = self.request_id + if self.summary is not None: + result['Summary'] = self.summary.to_map() + if self.total is not None: + result['Total'] = self.total return result def from_map(self, m: dict = None): m = m or dict() + self.job_metrics = [] + if m.get('JobMetrics') is not None: + for k in m.get('JobMetrics'): + temp_model = JobViewMetric() + self.job_metrics.append(temp_model.from_map(k)) if m.get('RequestId') is not None: self.request_id = m.get('RequestId') + if m.get('Summary') is not None: + temp_model = JobViewMetric() + self.summary = temp_model.from_map(m['Summary']) + if m.get('Total') is not None: + self.total = m.get('Total') return self -class DeleteTrainingJobLabelsResponse(TeaModel): +class GetJobViewMetricsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: DeleteTrainingJobLabelsResponseBody = None, + body: GetJobViewMetricsResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -7633,21 +8375,21 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = DeleteTrainingJobLabelsResponseBody() + temp_model = GetJobViewMetricsResponseBody() self.body = temp_model.from_map(m['body']) return self -class DeployLLMSnapshotRequestWorkloadContainer(TeaModel): +class GetJobsStatisticsByQuotaRequest(TeaModel): def __init__( self, - image: str = None, - port: int = None, - user_command: str = None, + end_time: str = None, + start_time: str = None, + workspace_id: str = None, ): - self.image = image - self.port = port - self.user_command = user_command + self.end_time = end_time + self.start_time = start_time + self.workspace_id = workspace_id def validate(self): pass @@ -7658,35 +8400,35 @@ def to_map(self): return _map result = dict() - if self.image is not None: - result['Image'] = self.image - if self.port is not None: - result['Port'] = self.port - if self.user_command is not None: - result['UserCommand'] = self.user_command + if self.end_time is not None: + result['EndTime'] = self.end_time + if self.start_time is not None: + result['StartTime'] = self.start_time + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Image') is not None: - self.image = m.get('Image') - if m.get('Port') is not None: - self.port = m.get('Port') - if m.get('UserCommand') is not None: - self.user_command = m.get('UserCommand') + if m.get('EndTime') is not None: + self.end_time = m.get('EndTime') + if m.get('StartTime') is not None: + self.start_time = m.get('StartTime') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class DeployLLMSnapshotRequestWorkloadExtraConfig(TeaModel): +class GetJobsStatisticsByQuotaResponseBody(TeaModel): def __init__( self, - enable_webservice: bool = None, - job_max_running_time_minutes: int = None, - third_party_lib_dir: str = None, + quota_id: str = None, + request_id: str = None, + statistics: Dict[str, Any] = None, ): - self.enable_webservice = enable_webservice - self.job_max_running_time_minutes = job_max_running_time_minutes - self.third_party_lib_dir = third_party_lib_dir + self.quota_id = quota_id + self.request_id = request_id + self.statistics = statistics def validate(self): pass @@ -7697,40 +8439,39 @@ def to_map(self): return _map result = dict() - if self.enable_webservice is not None: - result['EnableWebservice'] = self.enable_webservice - if self.job_max_running_time_minutes is not None: - result['JobMaxRunningTimeMinutes'] = self.job_max_running_time_minutes - if self.third_party_lib_dir is not None: - result['ThirdPartyLibDir'] = self.third_party_lib_dir + if self.quota_id is not None: + result['QuotaId'] = self.quota_id + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.statistics is not None: + result['Statistics'] = self.statistics return result def from_map(self, m: dict = None): m = m or dict() - if m.get('EnableWebservice') is not None: - self.enable_webservice = m.get('EnableWebservice') - if m.get('JobMaxRunningTimeMinutes') is not None: - self.job_max_running_time_minutes = m.get('JobMaxRunningTimeMinutes') - if m.get('ThirdPartyLibDir') is not None: - self.third_party_lib_dir = m.get('ThirdPartyLibDir') + if m.get('QuotaId') is not None: + self.quota_id = m.get('QuotaId') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('Statistics') is not None: + self.statistics = m.get('Statistics') return self -class DeployLLMSnapshotRequestWorkloadResourceSpecResourceConfig(TeaModel): +class GetJobsStatisticsByQuotaResponse(TeaModel): def __init__( self, - cpu: int = None, - gpu: int = None, - memory_in_gi_b: int = None, - resource_group: str = None, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetJobsStatisticsByQuotaResponseBody = None, ): - self.cpu = cpu - self.gpu = gpu - self.memory_in_gi_b = memory_in_gi_b - self.resource_group = resource_group + self.headers = headers + self.status_code = status_code + self.body = body def validate(self): - pass + if self.body: + self.body.validate() def to_map(self): _map = super().to_map() @@ -7738,84 +8479,36 @@ def to_map(self): return _map result = dict() - if self.cpu is not None: - result['Cpu'] = self.cpu - if self.gpu is not None: - result['Gpu'] = self.gpu - if self.memory_in_gi_b is not None: - result['MemoryInGiB'] = self.memory_in_gi_b - if self.resource_group is not None: - result['ResourceGroup'] = self.resource_group + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Cpu') is not None: - self.cpu = m.get('Cpu') - if m.get('Gpu') is not None: - self.gpu = m.get('Gpu') - if m.get('MemoryInGiB') is not None: - self.memory_in_gi_b = m.get('MemoryInGiB') - if m.get('ResourceGroup') is not None: - self.resource_group = m.get('ResourceGroup') - return self - - -class DeployLLMSnapshotRequestWorkloadResourceSpec(TeaModel): - def __init__( - self, - ecs_spec: str = None, - instance_num: int = None, - resource_config: DeployLLMSnapshotRequestWorkloadResourceSpecResourceConfig = None, - ): - self.ecs_spec = ecs_spec - self.instance_num = instance_num - self.resource_config = resource_config - - def validate(self): - if self.resource_config: - self.resource_config.validate() - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.ecs_spec is not None: - result['EcsSpec'] = self.ecs_spec - if self.instance_num is not None: - result['InstanceNum'] = self.instance_num - if self.resource_config is not None: - result['ResourceConfig'] = self.resource_config.to_map() - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('EcsSpec') is not None: - self.ecs_spec = m.get('EcsSpec') - if m.get('InstanceNum') is not None: - self.instance_num = m.get('InstanceNum') - if m.get('ResourceConfig') is not None: - temp_model = DeployLLMSnapshotRequestWorkloadResourceSpecResourceConfig() - self.resource_config = temp_model.from_map(m['ResourceConfig']) + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetJobsStatisticsByQuotaResponseBody() + self.body = temp_model.from_map(m['body']) return self -class DeployLLMSnapshotRequestWorkloadUserVpc(TeaModel): +class GetJobsStatisticsByResourceGroupRequest(TeaModel): def __init__( self, - default_route: str = None, - extended_cidrs: List[str] = None, - security_group_id: str = None, - switch_id: str = None, - vpc_id: str = None, + end_time: str = None, + start_time: str = None, + workspace_id: str = None, ): - self.default_route = default_route - self.extended_cidrs = extended_cidrs - self.security_group_id = security_group_id - self.switch_id = switch_id - self.vpc_id = vpc_id + self.end_time = end_time + self.start_time = start_time + self.workspace_id = workspace_id def validate(self): pass @@ -7826,55 +8519,36 @@ def to_map(self): return _map result = dict() - if self.default_route is not None: - result['DefaultRoute'] = self.default_route - if self.extended_cidrs is not None: - result['ExtendedCIDRs'] = self.extended_cidrs - if self.security_group_id is not None: - result['SecurityGroupId'] = self.security_group_id - if self.switch_id is not None: - result['SwitchId'] = self.switch_id - if self.vpc_id is not None: - result['VpcId'] = self.vpc_id + if self.end_time is not None: + result['EndTime'] = self.end_time + if self.start_time is not None: + result['StartTime'] = self.start_time + if self.workspace_id is not None: + result['WorkspaceID'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('DefaultRoute') is not None: - self.default_route = m.get('DefaultRoute') - if m.get('ExtendedCIDRs') is not None: - self.extended_cidrs = m.get('ExtendedCIDRs') - if m.get('SecurityGroupId') is not None: - self.security_group_id = m.get('SecurityGroupId') - if m.get('SwitchId') is not None: - self.switch_id = m.get('SwitchId') - if m.get('VpcId') is not None: - self.vpc_id = m.get('VpcId') + if m.get('EndTime') is not None: + self.end_time = m.get('EndTime') + if m.get('StartTime') is not None: + self.start_time = m.get('StartTime') + if m.get('WorkspaceID') is not None: + self.workspace_id = m.get('WorkspaceID') return self -class DeployLLMSnapshotRequestWorkload(TeaModel): +class GetJobsStatisticsByResourceGroupResponseBody(TeaModel): def __init__( self, - container: DeployLLMSnapshotRequestWorkloadContainer = None, - extra_config: DeployLLMSnapshotRequestWorkloadExtraConfig = None, - resource_spec: DeployLLMSnapshotRequestWorkloadResourceSpec = None, - user_vpc: DeployLLMSnapshotRequestWorkloadUserVpc = None, + request_id: str = None, + statistics: Dict[str, Any] = None, ): - self.container = container - self.extra_config = extra_config - self.resource_spec = resource_spec - self.user_vpc = user_vpc + self.request_id = request_id + self.statistics = statistics def validate(self): - if self.container: - self.container.validate() - if self.extra_config: - self.extra_config.validate() - if self.resource_spec: - self.resource_spec.validate() - if self.user_vpc: - self.user_vpc.validate() + pass def to_map(self): _map = super().to_map() @@ -7882,49 +8556,35 @@ def to_map(self): return _map result = dict() - if self.container is not None: - result['Container'] = self.container.to_map() - if self.extra_config is not None: - result['ExtraConfig'] = self.extra_config.to_map() - if self.resource_spec is not None: - result['ResourceSpec'] = self.resource_spec.to_map() - if self.user_vpc is not None: - result['UserVpc'] = self.user_vpc.to_map() + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.statistics is not None: + result['Statistics'] = self.statistics return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Container') is not None: - temp_model = DeployLLMSnapshotRequestWorkloadContainer() - self.container = temp_model.from_map(m['Container']) - if m.get('ExtraConfig') is not None: - temp_model = DeployLLMSnapshotRequestWorkloadExtraConfig() - self.extra_config = temp_model.from_map(m['ExtraConfig']) - if m.get('ResourceSpec') is not None: - temp_model = DeployLLMSnapshotRequestWorkloadResourceSpec() - self.resource_spec = temp_model.from_map(m['ResourceSpec']) - if m.get('UserVpc') is not None: - temp_model = DeployLLMSnapshotRequestWorkloadUserVpc() - self.user_vpc = temp_model.from_map(m['UserVpc']) + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('Statistics') is not None: + self.statistics = m.get('Statistics') return self -class DeployLLMSnapshotRequest(TeaModel): +class GetJobsStatisticsByResourceGroupResponse(TeaModel): def __init__( self, - description: str = None, - display_name: str = None, - labels: Dict[str, Any] = None, - workload: DeployLLMSnapshotRequestWorkload = None, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetJobsStatisticsByResourceGroupResponseBody = None, ): - self.description = description - self.display_name = display_name - self.labels = labels - self.workload = workload + self.headers = headers + self.status_code = status_code + self.body = body def validate(self): - if self.workload: - self.workload.validate() + if self.body: + self.body.validate() def to_map(self): _map = super().to_map() @@ -7932,44 +8592,66 @@ def to_map(self): return _map result = dict() - if self.description is not None: - result['Description'] = self.description - if self.display_name is not None: - result['DisplayName'] = self.display_name - if self.labels is not None: - result['Labels'] = self.labels - if self.workload is not None: - result['Workload'] = self.workload.to_map() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Description') is not None: - self.description = m.get('Description') - if m.get('DisplayName') is not None: - self.display_name = m.get('DisplayName') - if m.get('Labels') is not None: - self.labels = m.get('Labels') - if m.get('Workload') is not None: - temp_model = DeployLLMSnapshotRequestWorkload() - self.workload = temp_model.from_map(m['Workload']) + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetJobsStatisticsByResourceGroupResponseBody() + self.body = temp_model.from_map(m['body']) return self -class DeployLLMSnapshotResponseBody(TeaModel): +class GetMachineGroupResponseBody(TeaModel): def __init__( self, - job_id: str = None, - job_name: str = None, - job_request_id: str = None, + count: int = None, + default_driver: str = None, + duration: str = None, + ecs_type: str = None, + gmt_created: str = None, + gmt_expired: str = None, + gmt_modified: str = None, + gmt_started: str = None, + machine_group_id: str = None, + order_id: str = None, + order_instance_id: str = None, + pairesource_id: str = None, + pay_type: str = None, + pricing_cycle: str = None, + region_id: str = None, request_id: str = None, status: str = None, + supported_drivers: List[str] = None, ): - self.job_id = job_id - self.job_name = job_name - self.job_request_id = job_request_id + self.count = count + self.default_driver = default_driver + self.duration = duration + self.ecs_type = ecs_type + self.gmt_created = gmt_created + self.gmt_expired = gmt_expired + self.gmt_modified = gmt_modified + self.gmt_started = gmt_started + self.machine_group_id = machine_group_id + self.order_id = order_id + self.order_instance_id = order_instance_id + self.pairesource_id = pairesource_id + self.pay_type = pay_type + self.pricing_cycle = pricing_cycle + self.region_id = region_id self.request_id = request_id self.status = status + self.supported_drivers = supported_drivers def validate(self): pass @@ -7980,39 +8662,91 @@ def to_map(self): return _map result = dict() - if self.job_id is not None: - result['JobId'] = self.job_id - if self.job_name is not None: - result['JobName'] = self.job_name - if self.job_request_id is not None: - result['JobRequestId'] = self.job_request_id + if self.count is not None: + result['Count'] = self.count + if self.default_driver is not None: + result['DefaultDriver'] = self.default_driver + if self.duration is not None: + result['Duration'] = self.duration + if self.ecs_type is not None: + result['EcsType'] = self.ecs_type + if self.gmt_created is not None: + result['GmtCreated'] = self.gmt_created + if self.gmt_expired is not None: + result['GmtExpired'] = self.gmt_expired + if self.gmt_modified is not None: + result['GmtModified'] = self.gmt_modified + if self.gmt_started is not None: + result['GmtStarted'] = self.gmt_started + if self.machine_group_id is not None: + result['MachineGroupID'] = self.machine_group_id + if self.order_id is not None: + result['OrderID'] = self.order_id + if self.order_instance_id is not None: + result['OrderInstanceId'] = self.order_instance_id + if self.pairesource_id is not None: + result['PAIResourceID'] = self.pairesource_id + if self.pay_type is not None: + result['PayType'] = self.pay_type + if self.pricing_cycle is not None: + result['PricingCycle'] = self.pricing_cycle + if self.region_id is not None: + result['RegionID'] = self.region_id if self.request_id is not None: result['RequestId'] = self.request_id if self.status is not None: result['Status'] = self.status + if self.supported_drivers is not None: + result['SupportedDrivers'] = self.supported_drivers return result def from_map(self, m: dict = None): m = m or dict() - if m.get('JobId') is not None: - self.job_id = m.get('JobId') - if m.get('JobName') is not None: - self.job_name = m.get('JobName') - if m.get('JobRequestId') is not None: - self.job_request_id = m.get('JobRequestId') + if m.get('Count') is not None: + self.count = m.get('Count') + if m.get('DefaultDriver') is not None: + self.default_driver = m.get('DefaultDriver') + if m.get('Duration') is not None: + self.duration = m.get('Duration') + if m.get('EcsType') is not None: + self.ecs_type = m.get('EcsType') + if m.get('GmtCreated') is not None: + self.gmt_created = m.get('GmtCreated') + if m.get('GmtExpired') is not None: + self.gmt_expired = m.get('GmtExpired') + if m.get('GmtModified') is not None: + self.gmt_modified = m.get('GmtModified') + if m.get('GmtStarted') is not None: + self.gmt_started = m.get('GmtStarted') + if m.get('MachineGroupID') is not None: + self.machine_group_id = m.get('MachineGroupID') + if m.get('OrderID') is not None: + self.order_id = m.get('OrderID') + if m.get('OrderInstanceId') is not None: + self.order_instance_id = m.get('OrderInstanceId') + if m.get('PAIResourceID') is not None: + self.pairesource_id = m.get('PAIResourceID') + if m.get('PayType') is not None: + self.pay_type = m.get('PayType') + if m.get('PricingCycle') is not None: + self.pricing_cycle = m.get('PricingCycle') + if m.get('RegionID') is not None: + self.region_id = m.get('RegionID') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') if m.get('Status') is not None: self.status = m.get('Status') + if m.get('SupportedDrivers') is not None: + self.supported_drivers = m.get('SupportedDrivers') return self -class DeployLLMSnapshotResponse(TeaModel): +class GetMachineGroupResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: DeployLLMSnapshotResponseBody = None, + body: GetMachineGroupResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -8043,23 +8777,106 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = DeployLLMSnapshotResponseBody() + temp_model = GetMachineGroupResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetAI4DDefaultBucketResponseBody(TeaModel): +class GetMetricsRequest(TeaModel): def __init__( self, - extranet_endpoint: str = None, - intranet_endpoint: str = None, - name: str = None, - request_id: str = None, + dimensions: str = None, + end_time: str = None, + express: str = None, + length: str = None, + metric_name: str = None, + namespace: str = None, + next_token: str = None, + period: str = None, + start_time: str = None, ): - self.extranet_endpoint = extranet_endpoint - self.intranet_endpoint = intranet_endpoint - self.name = name + # This parameter is required. + self.dimensions = dimensions + self.end_time = end_time + self.express = express + self.length = length + # This parameter is required. + self.metric_name = metric_name + self.namespace = namespace + self.next_token = next_token + self.period = period + self.start_time = start_time + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.dimensions is not None: + result['Dimensions'] = self.dimensions + if self.end_time is not None: + result['EndTime'] = self.end_time + if self.express is not None: + result['Express'] = self.express + if self.length is not None: + result['Length'] = self.length + if self.metric_name is not None: + result['MetricName'] = self.metric_name + if self.namespace is not None: + result['Namespace'] = self.namespace + if self.next_token is not None: + result['NextToken'] = self.next_token + if self.period is not None: + result['Period'] = self.period + if self.start_time is not None: + result['StartTime'] = self.start_time + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Dimensions') is not None: + self.dimensions = m.get('Dimensions') + if m.get('EndTime') is not None: + self.end_time = m.get('EndTime') + if m.get('Express') is not None: + self.express = m.get('Express') + if m.get('Length') is not None: + self.length = m.get('Length') + if m.get('MetricName') is not None: + self.metric_name = m.get('MetricName') + if m.get('Namespace') is not None: + self.namespace = m.get('Namespace') + if m.get('NextToken') is not None: + self.next_token = m.get('NextToken') + if m.get('Period') is not None: + self.period = m.get('Period') + if m.get('StartTime') is not None: + self.start_time = m.get('StartTime') + return self + + +class GetMetricsResponseBody(TeaModel): + def __init__( + self, + code: str = None, + datapoints: str = None, + message: str = None, + next_token: str = None, + period: str = None, + request_id: str = None, + success: bool = None, + ): + self.code = code + self.datapoints = datapoints + self.message = message + self.next_token = next_token + self.period = period self.request_id = request_id + self.success = success def validate(self): pass @@ -8070,35 +8887,47 @@ def to_map(self): return _map result = dict() - if self.extranet_endpoint is not None: - result['ExtranetEndpoint'] = self.extranet_endpoint - if self.intranet_endpoint is not None: - result['IntranetEndpoint'] = self.intranet_endpoint - if self.name is not None: - result['Name'] = self.name + if self.code is not None: + result['Code'] = self.code + if self.datapoints is not None: + result['Datapoints'] = self.datapoints + if self.message is not None: + result['Message'] = self.message + if self.next_token is not None: + result['NextToken'] = self.next_token + if self.period is not None: + result['Period'] = self.period if self.request_id is not None: result['RequestId'] = self.request_id + if self.success is not None: + result['Success'] = self.success return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ExtranetEndpoint') is not None: - self.extranet_endpoint = m.get('ExtranetEndpoint') - if m.get('IntranetEndpoint') is not None: - self.intranet_endpoint = m.get('IntranetEndpoint') - if m.get('Name') is not None: - self.name = m.get('Name') + if m.get('Code') is not None: + self.code = m.get('Code') + if m.get('Datapoints') is not None: + self.datapoints = m.get('Datapoints') + if m.get('Message') is not None: + self.message = m.get('Message') + if m.get('NextToken') is not None: + self.next_token = m.get('NextToken') + if m.get('Period') is not None: + self.period = m.get('Period') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') + if m.get('Success') is not None: + self.success = m.get('Success') return self -class GetAI4DDefaultBucketResponse(TeaModel): +class GetMetricsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetAI4DDefaultBucketResponseBody = None, + body: GetMetricsResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -8129,40 +8958,72 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetAI4DDefaultBucketResponseBody() + temp_model = GetMetricsResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetAlgorithmResponseBody(TeaModel): +class GetNodeGPUMetricsRequest(TeaModel): def __init__( self, - algorithm_description: str = None, - algorithm_id: str = None, - algorithm_name: str = None, - algorithm_provider: str = None, - display_name: str = None, - gmt_create_time: str = None, - gmt_modified_time: str = None, + end_time: str = None, + metric_type: str = None, + quota_id: str = None, + start_time: str = None, + ): + self.end_time = end_time + # This parameter is required. + self.metric_type = metric_type + # This parameter is required. + self.quota_id = quota_id + self.start_time = start_time + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.end_time is not None: + result['EndTime'] = self.end_time + if self.metric_type is not None: + result['MetricType'] = self.metric_type + if self.quota_id is not None: + result['QuotaId'] = self.quota_id + if self.start_time is not None: + result['StartTime'] = self.start_time + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('EndTime') is not None: + self.end_time = m.get('EndTime') + if m.get('MetricType') is not None: + self.metric_type = m.get('MetricType') + if m.get('QuotaId') is not None: + self.quota_id = m.get('QuotaId') + if m.get('StartTime') is not None: + self.start_time = m.get('StartTime') + return self + + +class GetNodeGPUMetricsResponseBody(TeaModel): + def __init__( + self, + metric_type: str = None, + node_gpumetric: NodeGPUMetric = None, request_id: str = None, - tenant_id: str = None, - user_id: str = None, - workspace_id: str = None, ): - self.algorithm_description = algorithm_description - self.algorithm_id = algorithm_id - self.algorithm_name = algorithm_name - self.algorithm_provider = algorithm_provider - self.display_name = display_name - self.gmt_create_time = gmt_create_time - self.gmt_modified_time = gmt_modified_time + self.metric_type = metric_type + self.node_gpumetric = node_gpumetric self.request_id = request_id - self.tenant_id = tenant_id - self.user_id = user_id - self.workspace_id = workspace_id def validate(self): - pass + if self.node_gpumetric: + self.node_gpumetric.validate() def to_map(self): _map = super().to_map() @@ -8170,63 +9031,32 @@ def to_map(self): return _map result = dict() - if self.algorithm_description is not None: - result['AlgorithmDescription'] = self.algorithm_description - if self.algorithm_id is not None: - result['AlgorithmId'] = self.algorithm_id - if self.algorithm_name is not None: - result['AlgorithmName'] = self.algorithm_name - if self.algorithm_provider is not None: - result['AlgorithmProvider'] = self.algorithm_provider - if self.display_name is not None: - result['DisplayName'] = self.display_name - if self.gmt_create_time is not None: - result['GmtCreateTime'] = self.gmt_create_time - if self.gmt_modified_time is not None: - result['GmtModifiedTime'] = self.gmt_modified_time + if self.metric_type is not None: + result['MetricType'] = self.metric_type + if self.node_gpumetric is not None: + result['NodeGPUMetric'] = self.node_gpumetric.to_map() if self.request_id is not None: - result['RequestId'] = self.request_id - if self.tenant_id is not None: - result['TenantId'] = self.tenant_id - if self.user_id is not None: - result['UserId'] = self.user_id - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id + result['requestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('AlgorithmDescription') is not None: - self.algorithm_description = m.get('AlgorithmDescription') - if m.get('AlgorithmId') is not None: - self.algorithm_id = m.get('AlgorithmId') - if m.get('AlgorithmName') is not None: - self.algorithm_name = m.get('AlgorithmName') - if m.get('AlgorithmProvider') is not None: - self.algorithm_provider = m.get('AlgorithmProvider') - if m.get('DisplayName') is not None: - self.display_name = m.get('DisplayName') - if m.get('GmtCreateTime') is not None: - self.gmt_create_time = m.get('GmtCreateTime') - if m.get('GmtModifiedTime') is not None: - self.gmt_modified_time = m.get('GmtModifiedTime') - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - if m.get('TenantId') is not None: - self.tenant_id = m.get('TenantId') - if m.get('UserId') is not None: - self.user_id = m.get('UserId') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') + if m.get('MetricType') is not None: + self.metric_type = m.get('MetricType') + if m.get('NodeGPUMetric') is not None: + temp_model = NodeGPUMetric() + self.node_gpumetric = temp_model.from_map(m['NodeGPUMetric']) + if m.get('requestId') is not None: + self.request_id = m.get('requestId') return self -class GetAlgorithmResponse(TeaModel): +class GetNodeGPUMetricsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetAlgorithmResponseBody = None, + body: GetNodeGPUMetricsResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -8257,37 +9087,28 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetAlgorithmResponseBody() + temp_model = GetNodeGPUMetricsResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetAlgorithmVersionResponseBody(TeaModel): +class GetNodeMetricsRequest(TeaModel): def __init__( self, - algorithm_id: str = None, - algorithm_name: str = None, - algorithm_provider: str = None, - algorithm_spec: AlgorithmSpec = None, - algorithm_version: str = None, - gmt_create_time: str = None, - gmt_modified_time: str = None, - tenant_id: str = None, - user_id: str = None, + end_time: str = None, + gputype: str = None, + start_time: str = None, + time_step: str = None, + verbose: bool = None, ): - self.algorithm_id = algorithm_id - self.algorithm_name = algorithm_name - self.algorithm_provider = algorithm_provider - self.algorithm_spec = algorithm_spec - self.algorithm_version = algorithm_version - self.gmt_create_time = gmt_create_time - self.gmt_modified_time = gmt_modified_time - self.tenant_id = tenant_id - self.user_id = user_id + self.end_time = end_time + self.gputype = gputype + self.start_time = start_time + self.time_step = time_step + self.verbose = verbose def validate(self): - if self.algorithm_spec: - self.algorithm_spec.validate() + pass def to_map(self): _map = super().to_map() @@ -8295,56 +9116,86 @@ def to_map(self): return _map result = dict() - if self.algorithm_id is not None: - result['AlgorithmId'] = self.algorithm_id - if self.algorithm_name is not None: - result['AlgorithmName'] = self.algorithm_name - if self.algorithm_provider is not None: - result['AlgorithmProvider'] = self.algorithm_provider - if self.algorithm_spec is not None: - result['AlgorithmSpec'] = self.algorithm_spec.to_map() - if self.algorithm_version is not None: - result['AlgorithmVersion'] = self.algorithm_version - if self.gmt_create_time is not None: - result['GmtCreateTime'] = self.gmt_create_time - if self.gmt_modified_time is not None: - result['GmtModifiedTime'] = self.gmt_modified_time - if self.tenant_id is not None: - result['TenantId'] = self.tenant_id - if self.user_id is not None: - result['UserId'] = self.user_id - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('AlgorithmId') is not None: - self.algorithm_id = m.get('AlgorithmId') - if m.get('AlgorithmName') is not None: - self.algorithm_name = m.get('AlgorithmName') - if m.get('AlgorithmProvider') is not None: - self.algorithm_provider = m.get('AlgorithmProvider') - if m.get('AlgorithmSpec') is not None: - temp_model = AlgorithmSpec() - self.algorithm_spec = temp_model.from_map(m['AlgorithmSpec']) - if m.get('AlgorithmVersion') is not None: - self.algorithm_version = m.get('AlgorithmVersion') - if m.get('GmtCreateTime') is not None: - self.gmt_create_time = m.get('GmtCreateTime') - if m.get('GmtModifiedTime') is not None: - self.gmt_modified_time = m.get('GmtModifiedTime') - if m.get('TenantId') is not None: - self.tenant_id = m.get('TenantId') - if m.get('UserId') is not None: - self.user_id = m.get('UserId') + if self.end_time is not None: + result['EndTime'] = self.end_time + if self.gputype is not None: + result['GPUType'] = self.gputype + if self.start_time is not None: + result['StartTime'] = self.start_time + if self.time_step is not None: + result['TimeStep'] = self.time_step + if self.verbose is not None: + result['Verbose'] = self.verbose + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('EndTime') is not None: + self.end_time = m.get('EndTime') + if m.get('GPUType') is not None: + self.gputype = m.get('GPUType') + if m.get('StartTime') is not None: + self.start_time = m.get('StartTime') + if m.get('TimeStep') is not None: + self.time_step = m.get('TimeStep') + if m.get('Verbose') is not None: + self.verbose = m.get('Verbose') return self -class GetAlgorithmVersionResponse(TeaModel): +class GetNodeMetricsResponseBody(TeaModel): + def __init__( + self, + metric_type: str = None, + nodes_metrics: List[NodeMetric] = None, + resource_group_id: str = None, + ): + self.metric_type = metric_type + self.nodes_metrics = nodes_metrics + self.resource_group_id = resource_group_id + + def validate(self): + if self.nodes_metrics: + for k in self.nodes_metrics: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.metric_type is not None: + result['MetricType'] = self.metric_type + result['NodesMetrics'] = [] + if self.nodes_metrics is not None: + for k in self.nodes_metrics: + result['NodesMetrics'].append(k.to_map() if k else None) + if self.resource_group_id is not None: + result['ResourceGroupID'] = self.resource_group_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('MetricType') is not None: + self.metric_type = m.get('MetricType') + self.nodes_metrics = [] + if m.get('NodesMetrics') is not None: + for k in m.get('NodesMetrics'): + temp_model = NodeMetric() + self.nodes_metrics.append(temp_model.from_map(k)) + if m.get('ResourceGroupID') is not None: + self.resource_group_id = m.get('ResourceGroupID') + return self + + +class GetNodeMetricsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetAlgorithmVersionResponseBody = None, + body: GetNodeMetricsResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -8375,23 +9226,27 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetAlgorithmVersionResponseBody() + temp_model = GetNodeMetricsResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetComponentResponseBodyVersions(TeaModel): +class GetNodeViewMetricsRequest(TeaModel): def __init__( self, - gmt_create_time: str = None, - snapshot_id: str = None, - status: str = None, - version: str = None, + node_id: str = None, + page_number: int = None, + page_size: int = None, + time_step: str = None, + workspace_id: str = None, ): - self.gmt_create_time = gmt_create_time - self.snapshot_id = snapshot_id - self.status = status - self.version = version + self.node_id = node_id + # This parameter is required. + self.page_number = page_number + # This parameter is required. + self.page_size = page_size + self.time_step = time_step + self.workspace_id = workspace_id def validate(self): pass @@ -8402,67 +9257,45 @@ def to_map(self): return _map result = dict() - if self.gmt_create_time is not None: - result['GmtCreateTime'] = self.gmt_create_time - if self.snapshot_id is not None: - result['SnapshotId'] = self.snapshot_id - if self.status is not None: - result['Status'] = self.status - if self.version is not None: - result['Version'] = self.version + if self.node_id is not None: + result['NodeId'] = self.node_id + if self.page_number is not None: + result['PageNumber'] = self.page_number + if self.page_size is not None: + result['PageSize'] = self.page_size + if self.time_step is not None: + result['TimeStep'] = self.time_step + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('GmtCreateTime') is not None: - self.gmt_create_time = m.get('GmtCreateTime') - if m.get('SnapshotId') is not None: - self.snapshot_id = m.get('SnapshotId') - if m.get('Status') is not None: - self.status = m.get('Status') - if m.get('Version') is not None: - self.version = m.get('Version') + if m.get('NodeId') is not None: + self.node_id = m.get('NodeId') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + if m.get('TimeStep') is not None: + self.time_step = m.get('TimeStep') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class GetComponentResponseBody(TeaModel): +class GetNodeViewMetricsResponseBody(TeaModel): def __init__( self, - component_id: str = None, - description: str = None, - display_name: str = None, - gmt_create_time: str = None, - gmt_modified_time: str = None, - labels: List[Label] = None, - name: str = None, - provider: str = None, - request_id: str = None, - tenant_id: str = None, - user_id: str = None, - versions: List[GetComponentResponseBodyVersions] = None, - workspace_id: str = None, + node_metrics: List[NodeViewMetric] = None, + total: int = None, ): - self.component_id = component_id - self.description = description - self.display_name = display_name - self.gmt_create_time = gmt_create_time - self.gmt_modified_time = gmt_modified_time - self.labels = labels - self.name = name - self.provider = provider - self.request_id = request_id - self.tenant_id = tenant_id - self.user_id = user_id - self.versions = versions - self.workspace_id = workspace_id + self.node_metrics = node_metrics + self.total = total def validate(self): - if self.labels: - for k in self.labels: - if k: - k.validate() - if self.versions: - for k in self.versions: + if self.node_metrics: + for k in self.node_metrics: if k: k.validate() @@ -8472,81 +9305,32 @@ def to_map(self): return _map result = dict() - if self.component_id is not None: - result['ComponentId'] = self.component_id - if self.description is not None: - result['Description'] = self.description - if self.display_name is not None: - result['DisplayName'] = self.display_name - if self.gmt_create_time is not None: - result['GmtCreateTime'] = self.gmt_create_time - if self.gmt_modified_time is not None: - result['GmtModifiedTime'] = self.gmt_modified_time - result['Labels'] = [] - if self.labels is not None: - for k in self.labels: - result['Labels'].append(k.to_map() if k else None) - if self.name is not None: - result['Name'] = self.name - if self.provider is not None: - result['Provider'] = self.provider - if self.request_id is not None: - result['RequestId'] = self.request_id - if self.tenant_id is not None: - result['TenantId'] = self.tenant_id - if self.user_id is not None: - result['UserId'] = self.user_id - result['Versions'] = [] - if self.versions is not None: - for k in self.versions: - result['Versions'].append(k.to_map() if k else None) - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id + result['NodeMetrics'] = [] + if self.node_metrics is not None: + for k in self.node_metrics: + result['NodeMetrics'].append(k.to_map() if k else None) + if self.total is not None: + result['Total'] = self.total return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ComponentId') is not None: - self.component_id = m.get('ComponentId') - if m.get('Description') is not None: - self.description = m.get('Description') - if m.get('DisplayName') is not None: - self.display_name = m.get('DisplayName') - if m.get('GmtCreateTime') is not None: - self.gmt_create_time = m.get('GmtCreateTime') - if m.get('GmtModifiedTime') is not None: - self.gmt_modified_time = m.get('GmtModifiedTime') - self.labels = [] - if m.get('Labels') is not None: - for k in m.get('Labels'): - temp_model = Label() - self.labels.append(temp_model.from_map(k)) - if m.get('Name') is not None: - self.name = m.get('Name') - if m.get('Provider') is not None: - self.provider = m.get('Provider') - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - if m.get('TenantId') is not None: - self.tenant_id = m.get('TenantId') - if m.get('UserId') is not None: - self.user_id = m.get('UserId') - self.versions = [] - if m.get('Versions') is not None: - for k in m.get('Versions'): - temp_model = GetComponentResponseBodyVersions() - self.versions.append(temp_model.from_map(k)) - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') - return self + self.node_metrics = [] + if m.get('NodeMetrics') is not None: + for k in m.get('NodeMetrics'): + temp_model = NodeViewMetric() + self.node_metrics.append(temp_model.from_map(k)) + if m.get('Total') is not None: + self.total = m.get('Total') + return self -class GetComponentResponse(TeaModel): +class GetNodeViewMetricsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetComponentResponseBody = None, + body: GetNodeViewMetricsResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -8577,49 +9361,48 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetComponentResponseBody() + temp_model = GetNodeViewMetricsResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetComponentVersionResponseBody(TeaModel): +class GetOperationResponseBody(TeaModel): def __init__( self, - description: str = None, - gmt_create_time: str = None, + creator_id: str = None, + gmt_created_time: str = None, + gmt_end_time: str = None, gmt_modified_time: str = None, - labels: List[Label] = None, - name: str = None, - provider: str = None, + gmt_start_time: str = None, + object_id: str = None, + object_type: str = None, + operation_description: str = None, + operation_id: str = None, + operation_spec_json: str = None, + operation_type: str = None, + reason_code: str = None, + reason_message: str = None, request_id: str = None, - snapshot_id: str = None, - spec: ComponentSpec = None, - tenant_id: str = None, - user_id: str = None, - version: str = None, - workspace_id: str = None, + status: str = None, ): - self.description = description - self.gmt_create_time = gmt_create_time + self.creator_id = creator_id + self.gmt_created_time = gmt_created_time + self.gmt_end_time = gmt_end_time self.gmt_modified_time = gmt_modified_time - self.labels = labels - self.name = name - self.provider = provider + self.gmt_start_time = gmt_start_time + self.object_id = object_id + self.object_type = object_type + self.operation_description = operation_description + self.operation_id = operation_id + self.operation_spec_json = operation_spec_json + self.operation_type = operation_type + self.reason_code = reason_code + self.reason_message = reason_message self.request_id = request_id - self.snapshot_id = snapshot_id - self.spec = spec - self.tenant_id = tenant_id - self.user_id = user_id - self.version = version - self.workspace_id = workspace_id + self.status = status def validate(self): - if self.labels: - for k in self.labels: - if k: - k.validate() - if self.spec: - self.spec.validate() + pass def to_map(self): _map = super().to_map() @@ -8627,77 +9410,79 @@ def to_map(self): return _map result = dict() - if self.description is not None: - result['Description'] = self.description - if self.gmt_create_time is not None: - result['GmtCreateTime'] = self.gmt_create_time + if self.creator_id is not None: + result['CreatorId'] = self.creator_id + if self.gmt_created_time is not None: + result['GmtCreatedTime'] = self.gmt_created_time + if self.gmt_end_time is not None: + result['GmtEndTime'] = self.gmt_end_time if self.gmt_modified_time is not None: result['GmtModifiedTime'] = self.gmt_modified_time - result['Labels'] = [] - if self.labels is not None: - for k in self.labels: - result['Labels'].append(k.to_map() if k else None) - if self.name is not None: - result['Name'] = self.name - if self.provider is not None: - result['Provider'] = self.provider + if self.gmt_start_time is not None: + result['GmtStartTime'] = self.gmt_start_time + if self.object_id is not None: + result['ObjectId'] = self.object_id + if self.object_type is not None: + result['ObjectType'] = self.object_type + if self.operation_description is not None: + result['OperationDescription'] = self.operation_description + if self.operation_id is not None: + result['OperationId'] = self.operation_id + if self.operation_spec_json is not None: + result['OperationSpecJson'] = self.operation_spec_json + if self.operation_type is not None: + result['OperationType'] = self.operation_type + if self.reason_code is not None: + result['ReasonCode'] = self.reason_code + if self.reason_message is not None: + result['ReasonMessage'] = self.reason_message if self.request_id is not None: result['RequestId'] = self.request_id - if self.snapshot_id is not None: - result['SnapshotId'] = self.snapshot_id - if self.spec is not None: - result['Spec'] = self.spec.to_map() - if self.tenant_id is not None: - result['TenantId'] = self.tenant_id - if self.user_id is not None: - result['UserId'] = self.user_id - if self.version is not None: - result['Version'] = self.version - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id + if self.status is not None: + result['Status'] = self.status return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Description') is not None: - self.description = m.get('Description') - if m.get('GmtCreateTime') is not None: - self.gmt_create_time = m.get('GmtCreateTime') + if m.get('CreatorId') is not None: + self.creator_id = m.get('CreatorId') + if m.get('GmtCreatedTime') is not None: + self.gmt_created_time = m.get('GmtCreatedTime') + if m.get('GmtEndTime') is not None: + self.gmt_end_time = m.get('GmtEndTime') if m.get('GmtModifiedTime') is not None: self.gmt_modified_time = m.get('GmtModifiedTime') - self.labels = [] - if m.get('Labels') is not None: - for k in m.get('Labels'): - temp_model = Label() - self.labels.append(temp_model.from_map(k)) - if m.get('Name') is not None: - self.name = m.get('Name') - if m.get('Provider') is not None: - self.provider = m.get('Provider') + if m.get('GmtStartTime') is not None: + self.gmt_start_time = m.get('GmtStartTime') + if m.get('ObjectId') is not None: + self.object_id = m.get('ObjectId') + if m.get('ObjectType') is not None: + self.object_type = m.get('ObjectType') + if m.get('OperationDescription') is not None: + self.operation_description = m.get('OperationDescription') + if m.get('OperationId') is not None: + self.operation_id = m.get('OperationId') + if m.get('OperationSpecJson') is not None: + self.operation_spec_json = m.get('OperationSpecJson') + if m.get('OperationType') is not None: + self.operation_type = m.get('OperationType') + if m.get('ReasonCode') is not None: + self.reason_code = m.get('ReasonCode') + if m.get('ReasonMessage') is not None: + self.reason_message = m.get('ReasonMessage') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('SnapshotId') is not None: - self.snapshot_id = m.get('SnapshotId') - if m.get('Spec') is not None: - temp_model = ComponentSpec() - self.spec = temp_model.from_map(m['Spec']) - if m.get('TenantId') is not None: - self.tenant_id = m.get('TenantId') - if m.get('UserId') is not None: - self.user_id = m.get('UserId') - if m.get('Version') is not None: - self.version = m.get('Version') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') + if m.get('Status') is not None: + self.status = m.get('Status') return self -class GetComponentVersionResponse(TeaModel): +class GetOperationResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetComponentVersionResponseBody = None, + body: GetOperationResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -8728,35 +9513,31 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetComponentVersionResponseBody() + temp_model = GetOperationResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetComponentVersionSnapshotResponseBody(TeaModel): +class GetQueueInfosRequest(TeaModel): def __init__( self, - component_id: str = None, - gmt_create_time: str = None, - gmt_modified_time: str = None, - is_current_version: bool = None, - request_id: str = None, - snapshot_id: str = None, - tenant_id: str = None, - user_id: str = None, - version: str = None, - workspace_id: str = None, + order: str = None, + page_number: int = None, + page_size: int = None, + quota_ids: str = None, + sort_by: str = None, + workload_ids: str = None, + workload_type: str = None, + workspace_ids: str = None, ): - self.component_id = component_id - self.gmt_create_time = gmt_create_time - self.gmt_modified_time = gmt_modified_time - self.is_current_version = is_current_version - self.request_id = request_id - self.snapshot_id = snapshot_id - self.tenant_id = tenant_id - self.user_id = user_id - self.version = version - self.workspace_id = workspace_id + self.order = order + self.page_number = page_number + self.page_size = page_size + self.quota_ids = quota_ids + self.sort_by = sort_by + self.workload_ids = workload_ids + self.workload_type = workload_type + self.workspace_ids = workspace_ids def validate(self): pass @@ -8767,121 +9548,61 @@ def to_map(self): return _map result = dict() - if self.component_id is not None: - result['ComponentId'] = self.component_id - if self.gmt_create_time is not None: - result['GmtCreateTime'] = self.gmt_create_time - if self.gmt_modified_time is not None: - result['GmtModifiedTime'] = self.gmt_modified_time - if self.is_current_version is not None: - result['IsCurrentVersion'] = self.is_current_version - if self.request_id is not None: - result['RequestId'] = self.request_id - if self.snapshot_id is not None: - result['SnapshotId'] = self.snapshot_id - if self.tenant_id is not None: - result['TenantId'] = self.tenant_id - if self.user_id is not None: - result['UserId'] = self.user_id - if self.version is not None: - result['Version'] = self.version - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id + if self.order is not None: + result['Order'] = self.order + if self.page_number is not None: + result['PageNumber'] = self.page_number + if self.page_size is not None: + result['PageSize'] = self.page_size + if self.quota_ids is not None: + result['QuotaIds'] = self.quota_ids + if self.sort_by is not None: + result['SortBy'] = self.sort_by + if self.workload_ids is not None: + result['WorkloadIds'] = self.workload_ids + if self.workload_type is not None: + result['WorkloadType'] = self.workload_type + if self.workspace_ids is not None: + result['WorkspaceIds'] = self.workspace_ids return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ComponentId') is not None: - self.component_id = m.get('ComponentId') - if m.get('GmtCreateTime') is not None: - self.gmt_create_time = m.get('GmtCreateTime') - if m.get('GmtModifiedTime') is not None: - self.gmt_modified_time = m.get('GmtModifiedTime') - if m.get('IsCurrentVersion') is not None: - self.is_current_version = m.get('IsCurrentVersion') - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - if m.get('SnapshotId') is not None: - self.snapshot_id = m.get('SnapshotId') - if m.get('TenantId') is not None: - self.tenant_id = m.get('TenantId') - if m.get('UserId') is not None: - self.user_id = m.get('UserId') - if m.get('Version') is not None: - self.version = m.get('Version') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') - return self - - -class GetComponentVersionSnapshotResponse(TeaModel): - def __init__( - self, - headers: Dict[str, str] = None, - status_code: int = None, - body: GetComponentVersionSnapshotResponseBody = None, - ): - self.headers = headers - self.status_code = status_code - self.body = body - - def validate(self): - if self.body: - self.body.validate() - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.headers is not None: - result['headers'] = self.headers - if self.status_code is not None: - result['statusCode'] = self.status_code - if self.body is not None: - result['body'] = self.body.to_map() - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('headers') is not None: - self.headers = m.get('headers') - if m.get('statusCode') is not None: - self.status_code = m.get('statusCode') - if m.get('body') is not None: - temp_model = GetComponentVersionSnapshotResponseBody() - self.body = temp_model.from_map(m['body']) + if m.get('Order') is not None: + self.order = m.get('Order') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + if m.get('QuotaIds') is not None: + self.quota_ids = m.get('QuotaIds') + if m.get('SortBy') is not None: + self.sort_by = m.get('SortBy') + if m.get('WorkloadIds') is not None: + self.workload_ids = m.get('WorkloadIds') + if m.get('WorkloadType') is not None: + self.workload_type = m.get('WorkloadType') + if m.get('WorkspaceIds') is not None: + self.workspace_ids = m.get('WorkspaceIds') return self -class GetInstanceJobResponseBody(TeaModel): +class GetQueueInfosResponseBody(TeaModel): def __init__( self, - creator: str = None, - gmt_create_time: str = None, - instance_id: str = None, - instance_job_id: str = None, - instance_job_type: str = None, - reason_code: str = None, - reason_message: str = None, + queue_infos: List[QueueInfo] = None, request_id: str = None, - status: str = None, - workspace_id: str = None, + total_count: int = None, ): - self.creator = creator - self.gmt_create_time = gmt_create_time - self.instance_id = instance_id - self.instance_job_id = instance_job_id - self.instance_job_type = instance_job_type - self.reason_code = reason_code - self.reason_message = reason_message + self.queue_infos = queue_infos self.request_id = request_id - self.status = status - self.workspace_id = workspace_id + self.total_count = total_count def validate(self): - pass + if self.queue_infos: + for k in self.queue_infos: + if k: + k.validate() def to_map(self): _map = super().to_map() @@ -8889,59 +9610,36 @@ def to_map(self): return _map result = dict() - if self.creator is not None: - result['Creator'] = self.creator - if self.gmt_create_time is not None: - result['GmtCreateTime'] = self.gmt_create_time - if self.instance_id is not None: - result['InstanceId'] = self.instance_id - if self.instance_job_id is not None: - result['InstanceJobId'] = self.instance_job_id - if self.instance_job_type is not None: - result['InstanceJobType'] = self.instance_job_type - if self.reason_code is not None: - result['ReasonCode'] = self.reason_code - if self.reason_message is not None: - result['ReasonMessage'] = self.reason_message + result['QueueInfos'] = [] + if self.queue_infos is not None: + for k in self.queue_infos: + result['QueueInfos'].append(k.to_map() if k else None) if self.request_id is not None: result['RequestId'] = self.request_id - if self.status is not None: - result['Status'] = self.status - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id + if self.total_count is not None: + result['TotalCount'] = self.total_count return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Creator') is not None: - self.creator = m.get('Creator') - if m.get('GmtCreateTime') is not None: - self.gmt_create_time = m.get('GmtCreateTime') - if m.get('InstanceId') is not None: - self.instance_id = m.get('InstanceId') - if m.get('InstanceJobId') is not None: - self.instance_job_id = m.get('InstanceJobId') - if m.get('InstanceJobType') is not None: - self.instance_job_type = m.get('InstanceJobType') - if m.get('ReasonCode') is not None: - self.reason_code = m.get('ReasonCode') - if m.get('ReasonMessage') is not None: - self.reason_message = m.get('ReasonMessage') + self.queue_infos = [] + if m.get('QueueInfos') is not None: + for k in m.get('QueueInfos'): + temp_model = QueueInfo() + self.queue_infos.append(temp_model.from_map(k)) if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('Status') is not None: - self.status = m.get('Status') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') return self -class GetInstanceJobResponse(TeaModel): +class GetQueueInfosResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetInstanceJobResponseBody = None, + body: GetQueueInfosResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -8972,29 +9670,17 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetInstanceJobResponseBody() + temp_model = GetQueueInfosResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetJobViewMetricsRequest(TeaModel): +class GetQuotaRequest(TeaModel): def __init__( self, - end_time: str = None, - page_number: int = None, - page_size: int = None, - sort_by: str = None, - start_time: str = None, - time_step: str = None, - workspace_id: str = None, + verbose: bool = None, ): - self.end_time = end_time - self.page_number = page_number - self.page_size = page_size - self.sort_by = sort_by - self.start_time = start_time - self.time_step = time_step - self.workspace_id = workspace_id + self.verbose = verbose def validate(self): pass @@ -9005,61 +9691,86 @@ def to_map(self): return _map result = dict() - if self.end_time is not None: - result['EndTime'] = self.end_time - if self.page_number is not None: - result['PageNumber'] = self.page_number - if self.page_size is not None: - result['PageSize'] = self.page_size - if self.sort_by is not None: - result['SortBy'] = self.sort_by - if self.start_time is not None: - result['StartTime'] = self.start_time - if self.time_step is not None: - result['TimeStep'] = self.time_step - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id + if self.verbose is not None: + result['Verbose'] = self.verbose return result def from_map(self, m: dict = None): m = m or dict() - if m.get('EndTime') is not None: - self.end_time = m.get('EndTime') - if m.get('PageNumber') is not None: - self.page_number = m.get('PageNumber') - if m.get('PageSize') is not None: - self.page_size = m.get('PageSize') - if m.get('SortBy') is not None: - self.sort_by = m.get('SortBy') - if m.get('StartTime') is not None: - self.start_time = m.get('StartTime') - if m.get('TimeStep') is not None: - self.time_step = m.get('TimeStep') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') + if m.get('Verbose') is not None: + self.verbose = m.get('Verbose') return self -class GetJobViewMetricsResponseBody(TeaModel): +class GetQuotaResponseBody(TeaModel): def __init__( self, - job_metrics: List[JobViewMetric] = None, + allocate_strategy: str = None, + creator_id: str = None, + description: str = None, + gmt_created_time: str = None, + gmt_modified_time: str = None, + labels: List[Label] = None, + latest_operation_id: str = None, + min: ResourceSpec = None, + parent_quota_id: str = None, + queue_strategy: str = None, + quota_config: QuotaConfig = None, + quota_details: QuotaDetails = None, + quota_id: str = None, + quota_name: str = None, + reason_code: str = None, + reason_message: str = None, request_id: str = None, - summary: JobViewMetric = None, - total: int = None, + resource_group_ids: List[str] = None, + resource_type: str = None, + status: str = None, + sub_quotas: List[QuotaIdName] = None, + workspaces: List[WorkspaceIdName] = None, ): - self.job_metrics = job_metrics + self.allocate_strategy = allocate_strategy + self.creator_id = creator_id + self.description = description + self.gmt_created_time = gmt_created_time + self.gmt_modified_time = gmt_modified_time + self.labels = labels + self.latest_operation_id = latest_operation_id + self.min = min + self.parent_quota_id = parent_quota_id + self.queue_strategy = queue_strategy + self.quota_config = quota_config + self.quota_details = quota_details + # Quota Id + self.quota_id = quota_id + self.quota_name = quota_name + self.reason_code = reason_code + self.reason_message = reason_message self.request_id = request_id - self.summary = summary - self.total = total - - def validate(self): - if self.job_metrics: - for k in self.job_metrics: + self.resource_group_ids = resource_group_ids + self.resource_type = resource_type + self.status = status + self.sub_quotas = sub_quotas + self.workspaces = workspaces + + def validate(self): + if self.labels: + for k in self.labels: + if k: + k.validate() + if self.min: + self.min.validate() + if self.quota_config: + self.quota_config.validate() + if self.quota_details: + self.quota_details.validate() + if self.sub_quotas: + for k in self.sub_quotas: + if k: + k.validate() + if self.workspaces: + for k in self.workspaces: if k: k.validate() - if self.summary: - self.summary.validate() def to_map(self): _map = super().to_map() @@ -9067,41 +9778,125 @@ def to_map(self): return _map result = dict() - result['JobMetrics'] = [] - if self.job_metrics is not None: - for k in self.job_metrics: - result['JobMetrics'].append(k.to_map() if k else None) + if self.allocate_strategy is not None: + result['AllocateStrategy'] = self.allocate_strategy + if self.creator_id is not None: + result['CreatorId'] = self.creator_id + if self.description is not None: + result['Description'] = self.description + if self.gmt_created_time is not None: + result['GmtCreatedTime'] = self.gmt_created_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + if self.latest_operation_id is not None: + result['LatestOperationId'] = self.latest_operation_id + if self.min is not None: + result['Min'] = self.min.to_map() + if self.parent_quota_id is not None: + result['ParentQuotaId'] = self.parent_quota_id + if self.queue_strategy is not None: + result['QueueStrategy'] = self.queue_strategy + if self.quota_config is not None: + result['QuotaConfig'] = self.quota_config.to_map() + if self.quota_details is not None: + result['QuotaDetails'] = self.quota_details.to_map() + if self.quota_id is not None: + result['QuotaId'] = self.quota_id + if self.quota_name is not None: + result['QuotaName'] = self.quota_name + if self.reason_code is not None: + result['ReasonCode'] = self.reason_code + if self.reason_message is not None: + result['ReasonMessage'] = self.reason_message if self.request_id is not None: result['RequestId'] = self.request_id - if self.summary is not None: - result['Summary'] = self.summary.to_map() - if self.total is not None: - result['Total'] = self.total + if self.resource_group_ids is not None: + result['ResourceGroupIds'] = self.resource_group_ids + if self.resource_type is not None: + result['ResourceType'] = self.resource_type + if self.status is not None: + result['Status'] = self.status + result['SubQuotas'] = [] + if self.sub_quotas is not None: + for k in self.sub_quotas: + result['SubQuotas'].append(k.to_map() if k else None) + result['Workspaces'] = [] + if self.workspaces is not None: + for k in self.workspaces: + result['Workspaces'].append(k.to_map() if k else None) return result def from_map(self, m: dict = None): m = m or dict() - self.job_metrics = [] - if m.get('JobMetrics') is not None: - for k in m.get('JobMetrics'): - temp_model = JobViewMetric() - self.job_metrics.append(temp_model.from_map(k)) + if m.get('AllocateStrategy') is not None: + self.allocate_strategy = m.get('AllocateStrategy') + if m.get('CreatorId') is not None: + self.creator_id = m.get('CreatorId') + if m.get('Description') is not None: + self.description = m.get('Description') + if m.get('GmtCreatedTime') is not None: + self.gmt_created_time = m.get('GmtCreatedTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = Label() + self.labels.append(temp_model.from_map(k)) + if m.get('LatestOperationId') is not None: + self.latest_operation_id = m.get('LatestOperationId') + if m.get('Min') is not None: + temp_model = ResourceSpec() + self.min = temp_model.from_map(m['Min']) + if m.get('ParentQuotaId') is not None: + self.parent_quota_id = m.get('ParentQuotaId') + if m.get('QueueStrategy') is not None: + self.queue_strategy = m.get('QueueStrategy') + if m.get('QuotaConfig') is not None: + temp_model = QuotaConfig() + self.quota_config = temp_model.from_map(m['QuotaConfig']) + if m.get('QuotaDetails') is not None: + temp_model = QuotaDetails() + self.quota_details = temp_model.from_map(m['QuotaDetails']) + if m.get('QuotaId') is not None: + self.quota_id = m.get('QuotaId') + if m.get('QuotaName') is not None: + self.quota_name = m.get('QuotaName') + if m.get('ReasonCode') is not None: + self.reason_code = m.get('ReasonCode') + if m.get('ReasonMessage') is not None: + self.reason_message = m.get('ReasonMessage') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('Summary') is not None: - temp_model = JobViewMetric() - self.summary = temp_model.from_map(m['Summary']) - if m.get('Total') is not None: - self.total = m.get('Total') + if m.get('ResourceGroupIds') is not None: + self.resource_group_ids = m.get('ResourceGroupIds') + if m.get('ResourceType') is not None: + self.resource_type = m.get('ResourceType') + if m.get('Status') is not None: + self.status = m.get('Status') + self.sub_quotas = [] + if m.get('SubQuotas') is not None: + for k in m.get('SubQuotas'): + temp_model = QuotaIdName() + self.sub_quotas.append(temp_model.from_map(k)) + self.workspaces = [] + if m.get('Workspaces') is not None: + for k in m.get('Workspaces'): + temp_model = WorkspaceIdName() + self.workspaces.append(temp_model.from_map(k)) return self -class GetJobViewMetricsResponse(TeaModel): +class GetQuotaResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetJobViewMetricsResponseBody = None, + body: GetQuotaResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -9132,20 +9927,30 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetJobViewMetricsResponseBody() + temp_model = GetQuotaResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetJobsStatisticsByQuotaRequest(TeaModel): +class GetQuotaJobViewMetricsRequest(TeaModel): def __init__( self, end_time: str = None, + order: str = None, + page_number: int = None, + page_size: int = None, + sort_by: str = None, start_time: str = None, + time_step: str = None, workspace_id: str = None, ): self.end_time = end_time + self.order = order + self.page_number = page_number + self.page_size = page_size + self.sort_by = sort_by self.start_time = start_time + self.time_step = time_step self.workspace_id = workspace_id def validate(self): @@ -9159,8 +9964,18 @@ def to_map(self): result = dict() if self.end_time is not None: result['EndTime'] = self.end_time + if self.order is not None: + result['Order'] = self.order + if self.page_number is not None: + result['PageNumber'] = self.page_number + if self.page_size is not None: + result['PageSize'] = self.page_size + if self.sort_by is not None: + result['SortBy'] = self.sort_by if self.start_time is not None: result['StartTime'] = self.start_time + if self.time_step is not None: + result['TimeStep'] = self.time_step if self.workspace_id is not None: result['WorkspaceId'] = self.workspace_id return result @@ -9169,26 +9984,45 @@ def from_map(self, m: dict = None): m = m or dict() if m.get('EndTime') is not None: self.end_time = m.get('EndTime') + if m.get('Order') is not None: + self.order = m.get('Order') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + if m.get('SortBy') is not None: + self.sort_by = m.get('SortBy') if m.get('StartTime') is not None: self.start_time = m.get('StartTime') + if m.get('TimeStep') is not None: + self.time_step = m.get('TimeStep') if m.get('WorkspaceId') is not None: self.workspace_id = m.get('WorkspaceId') return self -class GetJobsStatisticsByQuotaResponseBody(TeaModel): +class GetQuotaJobViewMetricsResponseBody(TeaModel): def __init__( self, + job_metrics: List[QuotaJobViewMetric] = None, quota_id: str = None, request_id: str = None, - statistics: Dict[str, Any] = None, + summary: QuotaJobViewMetric = None, + total_count: int = None, ): + self.job_metrics = job_metrics self.quota_id = quota_id self.request_id = request_id - self.statistics = statistics + self.summary = summary + self.total_count = total_count def validate(self): - pass + if self.job_metrics: + for k in self.job_metrics: + if k: + k.validate() + if self.summary: + self.summary.validate() def to_map(self): _map = super().to_map() @@ -9196,31 +10030,45 @@ def to_map(self): return _map result = dict() + result['JobMetrics'] = [] + if self.job_metrics is not None: + for k in self.job_metrics: + result['JobMetrics'].append(k.to_map() if k else None) if self.quota_id is not None: result['QuotaId'] = self.quota_id if self.request_id is not None: result['RequestId'] = self.request_id - if self.statistics is not None: - result['Statistics'] = self.statistics + if self.summary is not None: + result['Summary'] = self.summary.to_map() + if self.total_count is not None: + result['TotalCount'] = self.total_count return result def from_map(self, m: dict = None): m = m or dict() + self.job_metrics = [] + if m.get('JobMetrics') is not None: + for k in m.get('JobMetrics'): + temp_model = QuotaJobViewMetric() + self.job_metrics.append(temp_model.from_map(k)) if m.get('QuotaId') is not None: self.quota_id = m.get('QuotaId') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('Statistics') is not None: - self.statistics = m.get('Statistics') + if m.get('Summary') is not None: + temp_model = QuotaJobViewMetric() + self.summary = temp_model.from_map(m['Summary']) + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') return self -class GetJobsStatisticsByQuotaResponse(TeaModel): +class GetQuotaJobViewMetricsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetJobsStatisticsByQuotaResponseBody = None, + body: GetQuotaJobViewMetricsResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -9251,21 +10099,23 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetJobsStatisticsByQuotaResponseBody() + temp_model = GetQuotaJobViewMetricsResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetJobsStatisticsByResourceGroupRequest(TeaModel): +class GetQuotaMetricsRequest(TeaModel): def __init__( self, end_time: str = None, + gputype: str = None, start_time: str = None, - workspace_id: str = None, + time_step: str = None, ): self.end_time = end_time + self.gputype = gputype self.start_time = start_time - self.workspace_id = workspace_id + self.time_step = time_step def validate(self): pass @@ -9278,34 +10128,43 @@ def to_map(self): result = dict() if self.end_time is not None: result['EndTime'] = self.end_time + if self.gputype is not None: + result['GPUType'] = self.gputype if self.start_time is not None: result['StartTime'] = self.start_time - if self.workspace_id is not None: - result['WorkspaceID'] = self.workspace_id + if self.time_step is not None: + result['TimeStep'] = self.time_step return result def from_map(self, m: dict = None): m = m or dict() if m.get('EndTime') is not None: self.end_time = m.get('EndTime') + if m.get('GPUType') is not None: + self.gputype = m.get('GPUType') if m.get('StartTime') is not None: self.start_time = m.get('StartTime') - if m.get('WorkspaceID') is not None: - self.workspace_id = m.get('WorkspaceID') + if m.get('TimeStep') is not None: + self.time_step = m.get('TimeStep') return self -class GetJobsStatisticsByResourceGroupResponseBody(TeaModel): +class GetQuotaMetricsResponseBody(TeaModel): def __init__( self, + quota_id: str = None, + quota_metrics: List[QuotaMetric] = None, request_id: str = None, - statistics: Dict[str, Any] = None, ): + self.quota_id = quota_id + self.quota_metrics = quota_metrics self.request_id = request_id - self.statistics = statistics def validate(self): - pass + if self.quota_metrics: + for k in self.quota_metrics: + if k: + k.validate() def to_map(self): _map = super().to_map() @@ -9313,27 +10172,36 @@ def to_map(self): return _map result = dict() + if self.quota_id is not None: + result['QuotaId'] = self.quota_id + result['QuotaMetrics'] = [] + if self.quota_metrics is not None: + for k in self.quota_metrics: + result['QuotaMetrics'].append(k.to_map() if k else None) if self.request_id is not None: result['RequestId'] = self.request_id - if self.statistics is not None: - result['Statistics'] = self.statistics return result def from_map(self, m: dict = None): m = m or dict() + if m.get('QuotaId') is not None: + self.quota_id = m.get('QuotaId') + self.quota_metrics = [] + if m.get('QuotaMetrics') is not None: + for k in m.get('QuotaMetrics'): + temp_model = QuotaMetric() + self.quota_metrics.append(temp_model.from_map(k)) if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('Statistics') is not None: - self.statistics = m.get('Statistics') return self -class GetJobsStatisticsByResourceGroupResponse(TeaModel): +class GetQuotaMetricsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetJobsStatisticsByResourceGroupResponseBody = None, + body: GetQuotaMetricsResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -9364,52 +10232,25 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetJobsStatisticsByResourceGroupResponseBody() + temp_model = GetQuotaMetricsResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetLLMProjectResponseBodyLabels(TeaModel): - def __init__( - self, - key: str = None, - value: str = None, - ): - self.key = key - self.value = value - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.key is not None: - result['Key'] = self.key - if self.value is not None: - result['Value'] = self.value - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('Key') is not None: - self.key = m.get('Key') - if m.get('Value') is not None: - self.value = m.get('Value') - return self - - -class GetLLMProjectResponseBodyRuntime(TeaModel): +class GetQuotaNodeMetricsRequest(TeaModel): def __init__( self, - runtime_id: str = None, - runtime_type: str = None, + end_time: str = None, + gputype: str = None, + start_time: str = None, + time_step: str = None, + verbose: bool = None, ): - self.runtime_id = runtime_id - self.runtime_type = runtime_type + self.end_time = end_time + self.gputype = gputype + self.start_time = start_time + self.time_step = time_step + self.verbose = verbose def validate(self): pass @@ -9420,59 +10261,51 @@ def to_map(self): return _map result = dict() - if self.runtime_id is not None: - result['RuntimeId'] = self.runtime_id - if self.runtime_type is not None: - result['RuntimeType'] = self.runtime_type + if self.end_time is not None: + result['EndTime'] = self.end_time + if self.gputype is not None: + result['GPUType'] = self.gputype + if self.start_time is not None: + result['StartTime'] = self.start_time + if self.time_step is not None: + result['TimeStep'] = self.time_step + if self.verbose is not None: + result['Verbose'] = self.verbose return result def from_map(self, m: dict = None): m = m or dict() - if m.get('RuntimeId') is not None: - self.runtime_id = m.get('RuntimeId') - if m.get('RuntimeType') is not None: - self.runtime_type = m.get('RuntimeType') + if m.get('EndTime') is not None: + self.end_time = m.get('EndTime') + if m.get('GPUType') is not None: + self.gputype = m.get('GPUType') + if m.get('StartTime') is not None: + self.start_time = m.get('StartTime') + if m.get('TimeStep') is not None: + self.time_step = m.get('TimeStep') + if m.get('Verbose') is not None: + self.verbose = m.get('Verbose') return self -class GetLLMProjectResponseBody(TeaModel): +class GetQuotaNodeMetricsResponseBody(TeaModel): def __init__( self, - gmt_create_time: str = None, - gmt_modified_time: str = None, - labels: List[GetLLMProjectResponseBodyLabels] = None, - owner_id: str = None, - project_description: str = None, - project_id: str = None, - project_name: str = None, - project_type: str = None, + metric_type: str = None, + nodes_metrics: List[NodeMetric] = None, + quota_id: str = None, request_id: str = None, - root_path: str = None, - runtime: GetLLMProjectResponseBodyRuntime = None, - user_id: str = None, - workspace_id: str = None, ): - self.gmt_create_time = gmt_create_time - self.gmt_modified_time = gmt_modified_time - self.labels = labels - self.owner_id = owner_id - self.project_description = project_description - self.project_id = project_id - self.project_name = project_name - self.project_type = project_type + self.metric_type = metric_type + self.nodes_metrics = nodes_metrics + self.quota_id = quota_id self.request_id = request_id - self.root_path = root_path - self.runtime = runtime - self.user_id = user_id - self.workspace_id = workspace_id def validate(self): - if self.labels: - for k in self.labels: + if self.nodes_metrics: + for k in self.nodes_metrics: if k: k.validate() - if self.runtime: - self.runtime.validate() def to_map(self): _map = super().to_map() @@ -9480,77 +10313,40 @@ def to_map(self): return _map result = dict() - if self.gmt_create_time is not None: - result['GmtCreateTime'] = self.gmt_create_time - if self.gmt_modified_time is not None: - result['GmtModifiedTime'] = self.gmt_modified_time - result['Labels'] = [] - if self.labels is not None: - for k in self.labels: - result['Labels'].append(k.to_map() if k else None) - if self.owner_id is not None: - result['OwnerId'] = self.owner_id - if self.project_description is not None: - result['ProjectDescription'] = self.project_description - if self.project_id is not None: - result['ProjectId'] = self.project_id - if self.project_name is not None: - result['ProjectName'] = self.project_name - if self.project_type is not None: - result['ProjectType'] = self.project_type + if self.metric_type is not None: + result['MetricType'] = self.metric_type + result['NodesMetrics'] = [] + if self.nodes_metrics is not None: + for k in self.nodes_metrics: + result['NodesMetrics'].append(k.to_map() if k else None) + if self.quota_id is not None: + result['QuotaId'] = self.quota_id if self.request_id is not None: result['RequestId'] = self.request_id - if self.root_path is not None: - result['RootPath'] = self.root_path - if self.runtime is not None: - result['Runtime'] = self.runtime.to_map() - if self.user_id is not None: - result['UserId'] = self.user_id - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('GmtCreateTime') is not None: - self.gmt_create_time = m.get('GmtCreateTime') - if m.get('GmtModifiedTime') is not None: - self.gmt_modified_time = m.get('GmtModifiedTime') - self.labels = [] - if m.get('Labels') is not None: - for k in m.get('Labels'): - temp_model = GetLLMProjectResponseBodyLabels() - self.labels.append(temp_model.from_map(k)) - if m.get('OwnerId') is not None: - self.owner_id = m.get('OwnerId') - if m.get('ProjectDescription') is not None: - self.project_description = m.get('ProjectDescription') - if m.get('ProjectId') is not None: - self.project_id = m.get('ProjectId') - if m.get('ProjectName') is not None: - self.project_name = m.get('ProjectName') - if m.get('ProjectType') is not None: - self.project_type = m.get('ProjectType') + if m.get('MetricType') is not None: + self.metric_type = m.get('MetricType') + self.nodes_metrics = [] + if m.get('NodesMetrics') is not None: + for k in m.get('NodesMetrics'): + temp_model = NodeMetric() + self.nodes_metrics.append(temp_model.from_map(k)) + if m.get('QuotaId') is not None: + self.quota_id = m.get('QuotaId') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('RootPath') is not None: - self.root_path = m.get('RootPath') - if m.get('Runtime') is not None: - temp_model = GetLLMProjectResponseBodyRuntime() - self.runtime = temp_model.from_map(m['Runtime']) - if m.get('UserId') is not None: - self.user_id = m.get('UserId') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') return self -class GetLLMProjectResponse(TeaModel): +class GetQuotaNodeMetricsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetLLMProjectResponseBody = None, + body: GetQuotaNodeMetricsResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -9581,21 +10377,37 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetLLMProjectResponseBody() + temp_model = GetQuotaNodeMetricsResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetLLMServiceIdentityRoleResponseBody(TeaModel): +class GetQuotaNodeViewMetricsRequest(TeaModel): def __init__( self, - exist: bool = None, - request_id: str = None, - role_name: str = None, + node_id: str = None, + node_status: str = None, + order: str = None, + order_status: str = None, + page_number: int = None, + page_size: int = None, + resource_group_id: str = None, + self_only: bool = None, + sort_by: str = None, + time_step: str = None, + workspace_id: str = None, ): - self.exist = exist - self.request_id = request_id - self.role_name = role_name + self.node_id = node_id + self.node_status = node_status + self.order = order + self.order_status = order_status + self.page_number = page_number + self.page_size = page_size + self.resource_group_id = resource_group_id + self.self_only = self_only + self.sort_by = sort_by + self.time_step = time_step + self.workspace_id = workspace_id def validate(self): pass @@ -9606,38 +10418,123 @@ def to_map(self): return _map result = dict() - if self.exist is not None: - result['Exist'] = self.exist - if self.request_id is not None: - result['RequestId'] = self.request_id - if self.role_name is not None: - result['RoleName'] = self.role_name + if self.node_id is not None: + result['NodeId'] = self.node_id + if self.node_status is not None: + result['NodeStatus'] = self.node_status + if self.order is not None: + result['Order'] = self.order + if self.order_status is not None: + result['OrderStatus'] = self.order_status + if self.page_number is not None: + result['PageNumber'] = self.page_number + if self.page_size is not None: + result['PageSize'] = self.page_size + if self.resource_group_id is not None: + result['ResourceGroupId'] = self.resource_group_id + if self.self_only is not None: + result['SelfOnly'] = self.self_only + if self.sort_by is not None: + result['SortBy'] = self.sort_by + if self.time_step is not None: + result['TimeStep'] = self.time_step + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Exist') is not None: - self.exist = m.get('Exist') - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - if m.get('RoleName') is not None: - self.role_name = m.get('RoleName') + if m.get('NodeId') is not None: + self.node_id = m.get('NodeId') + if m.get('NodeStatus') is not None: + self.node_status = m.get('NodeStatus') + if m.get('Order') is not None: + self.order = m.get('Order') + if m.get('OrderStatus') is not None: + self.order_status = m.get('OrderStatus') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + if m.get('ResourceGroupId') is not None: + self.resource_group_id = m.get('ResourceGroupId') + if m.get('SelfOnly') is not None: + self.self_only = m.get('SelfOnly') + if m.get('SortBy') is not None: + self.sort_by = m.get('SortBy') + if m.get('TimeStep') is not None: + self.time_step = m.get('TimeStep') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class GetLLMServiceIdentityRoleResponse(TeaModel): +class GetQuotaNodeViewMetricsResponseBody(TeaModel): def __init__( self, - headers: Dict[str, str] = None, - status_code: int = None, - body: GetLLMServiceIdentityRoleResponseBody = None, + node_metrics: List[QuotaNodeViewMetric] = None, + quota_id: str = None, + request_id: str = None, + total_count: int = None, ): - self.headers = headers - self.status_code = status_code - self.body = body + self.node_metrics = node_metrics + self.quota_id = quota_id + self.request_id = request_id + self.total_count = total_count def validate(self): - if self.body: + if self.node_metrics: + for k in self.node_metrics: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + result['NodeMetrics'] = [] + if self.node_metrics is not None: + for k in self.node_metrics: + result['NodeMetrics'].append(k.to_map() if k else None) + if self.quota_id is not None: + result['QuotaId'] = self.quota_id + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.total_count is not None: + result['TotalCount'] = self.total_count + return result + + def from_map(self, m: dict = None): + m = m or dict() + self.node_metrics = [] + if m.get('NodeMetrics') is not None: + for k in m.get('NodeMetrics'): + temp_model = QuotaNodeViewMetric() + self.node_metrics.append(temp_model.from_map(k)) + if m.get('QuotaId') is not None: + self.quota_id = m.get('QuotaId') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') + return self + + +class GetQuotaNodeViewMetricsResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetQuotaNodeViewMetricsResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: self.body.validate() def to_map(self): @@ -9661,19 +10558,37 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetLLMServiceIdentityRoleResponseBody() + temp_model = GetQuotaNodeViewMetricsResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetLLMSnapshotResponseBodyContentStorage(TeaModel): +class GetQuotaQueueInfoRequest(TeaModel): def __init__( self, - location: str = None, - type: str = None, + before_workload_id: str = None, + order: str = None, + page_number: int = None, + page_size: int = None, + show_own: bool = None, + sort_by: str = None, + status: str = None, + sub_quota_ids: str = None, + workload_ids: str = None, + workload_type: str = None, + workspace_ids: str = None, ): - self.location = location - self.type = type + self.before_workload_id = before_workload_id + self.order = order + self.page_number = page_number + self.page_size = page_size + self.show_own = show_own + self.sort_by = sort_by + self.status = status + self.sub_quota_ids = sub_quota_ids + self.workload_ids = workload_ids + self.workload_type = workload_type + self.workspace_ids = workspace_ids def validate(self): pass @@ -9684,45 +10599,73 @@ def to_map(self): return _map result = dict() - if self.location is not None: - result['Location'] = self.location - if self.type is not None: - result['Type'] = self.type + if self.before_workload_id is not None: + result['BeforeWorkloadId'] = self.before_workload_id + if self.order is not None: + result['Order'] = self.order + if self.page_number is not None: + result['PageNumber'] = self.page_number + if self.page_size is not None: + result['PageSize'] = self.page_size + if self.show_own is not None: + result['ShowOwn'] = self.show_own + if self.sort_by is not None: + result['SortBy'] = self.sort_by + if self.status is not None: + result['Status'] = self.status + if self.sub_quota_ids is not None: + result['SubQuotaIds'] = self.sub_quota_ids + if self.workload_ids is not None: + result['WorkloadIds'] = self.workload_ids + if self.workload_type is not None: + result['WorkloadType'] = self.workload_type + if self.workspace_ids is not None: + result['WorkspaceIds'] = self.workspace_ids return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Location') is not None: - self.location = m.get('Location') - if m.get('Type') is not None: - self.type = m.get('Type') + if m.get('BeforeWorkloadId') is not None: + self.before_workload_id = m.get('BeforeWorkloadId') + if m.get('Order') is not None: + self.order = m.get('Order') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + if m.get('ShowOwn') is not None: + self.show_own = m.get('ShowOwn') + if m.get('SortBy') is not None: + self.sort_by = m.get('SortBy') + if m.get('Status') is not None: + self.status = m.get('Status') + if m.get('SubQuotaIds') is not None: + self.sub_quota_ids = m.get('SubQuotaIds') + if m.get('WorkloadIds') is not None: + self.workload_ids = m.get('WorkloadIds') + if m.get('WorkloadType') is not None: + self.workload_type = m.get('WorkloadType') + if m.get('WorkspaceIds') is not None: + self.workspace_ids = m.get('WorkspaceIds') return self -class GetLLMSnapshotResponseBody(TeaModel): +class GetQuotaQueueInfoResponseBody(TeaModel): def __init__( self, - content_storage: GetLLMSnapshotResponseBodyContentStorage = None, - gmt_create_time: str = None, - gmt_modified_time: str = None, - owner_id: str = None, - project_id: str = None, + queue_infos: List[QueueInfo] = None, request_id: str = None, - snapshot_id: str = None, - user_id: str = None, + total_count: int = None, ): - self.content_storage = content_storage - self.gmt_create_time = gmt_create_time - self.gmt_modified_time = gmt_modified_time - self.owner_id = owner_id - self.project_id = project_id + self.queue_infos = queue_infos self.request_id = request_id - self.snapshot_id = snapshot_id - self.user_id = user_id + self.total_count = total_count def validate(self): - if self.content_storage: - self.content_storage.validate() + if self.queue_infos: + for k in self.queue_infos: + if k: + k.validate() def to_map(self): _map = super().to_map() @@ -9730,52 +10673,36 @@ def to_map(self): return _map result = dict() - if self.content_storage is not None: - result['ContentStorage'] = self.content_storage.to_map() - if self.gmt_create_time is not None: - result['GmtCreateTime'] = self.gmt_create_time - if self.gmt_modified_time is not None: - result['GmtModifiedTime'] = self.gmt_modified_time - if self.owner_id is not None: - result['OwnerId'] = self.owner_id - if self.project_id is not None: - result['ProjectId'] = self.project_id + result['QueueInfos'] = [] + if self.queue_infos is not None: + for k in self.queue_infos: + result['QueueInfos'].append(k.to_map() if k else None) if self.request_id is not None: result['RequestId'] = self.request_id - if self.snapshot_id is not None: - result['SnapshotId'] = self.snapshot_id - if self.user_id is not None: - result['UserId'] = self.user_id + if self.total_count is not None: + result['TotalCount'] = self.total_count return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ContentStorage') is not None: - temp_model = GetLLMSnapshotResponseBodyContentStorage() - self.content_storage = temp_model.from_map(m['ContentStorage']) - if m.get('GmtCreateTime') is not None: - self.gmt_create_time = m.get('GmtCreateTime') - if m.get('GmtModifiedTime') is not None: - self.gmt_modified_time = m.get('GmtModifiedTime') - if m.get('OwnerId') is not None: - self.owner_id = m.get('OwnerId') - if m.get('ProjectId') is not None: - self.project_id = m.get('ProjectId') + self.queue_infos = [] + if m.get('QueueInfos') is not None: + for k in m.get('QueueInfos'): + temp_model = QueueInfo() + self.queue_infos.append(temp_model.from_map(k)) if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('SnapshotId') is not None: - self.snapshot_id = m.get('SnapshotId') - if m.get('UserId') is not None: - self.user_id = m.get('UserId') + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') return self -class GetLLMSnapshotResponse(TeaModel): +class GetQuotaQueueInfoResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetLLMSnapshotResponseBody = None, + body: GetQuotaQueueInfoResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -9806,49 +10733,31 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetLLMSnapshotResponseBody() + temp_model = GetQuotaQueueInfoResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetMachineGroupResponseBody(TeaModel): +class GetQuotaRangeUserViewMetricsRequest(TeaModel): def __init__( self, - count: int = None, - default_driver: str = None, - duration: str = None, - ecs_type: str = None, - gmt_created: str = None, - gmt_expired: str = None, - gmt_modified: str = None, - gmt_started: str = None, - machine_group_id: str = None, - order_id: str = None, - pairesource_id: str = None, - pay_type: str = None, - pricing_cycle: str = None, - region_id: str = None, - request_id: str = None, - status: str = None, - supported_drivers: List[str] = None, + end_time: str = None, + order: str = None, + page_number: int = None, + page_size: int = None, + sort_by: str = None, + start_time: str = None, + user_id: str = None, + workspace_id: str = None, ): - self.count = count - self.default_driver = default_driver - self.duration = duration - self.ecs_type = ecs_type - self.gmt_created = gmt_created - self.gmt_expired = gmt_expired - self.gmt_modified = gmt_modified - self.gmt_started = gmt_started - self.machine_group_id = machine_group_id - self.order_id = order_id - self.pairesource_id = pairesource_id - self.pay_type = pay_type - self.pricing_cycle = pricing_cycle - self.region_id = region_id - self.request_id = request_id - self.status = status - self.supported_drivers = supported_drivers + self.end_time = end_time + self.order = order + self.page_number = page_number + self.page_size = page_size + self.sort_by = sort_by + self.start_time = start_time + self.user_id = user_id + self.workspace_id = workspace_id def validate(self): pass @@ -9859,87 +10768,113 @@ def to_map(self): return _map result = dict() - if self.count is not None: - result['Count'] = self.count - if self.default_driver is not None: - result['DefaultDriver'] = self.default_driver - if self.duration is not None: - result['Duration'] = self.duration - if self.ecs_type is not None: - result['EcsType'] = self.ecs_type - if self.gmt_created is not None: - result['GmtCreated'] = self.gmt_created - if self.gmt_expired is not None: - result['GmtExpired'] = self.gmt_expired - if self.gmt_modified is not None: - result['GmtModified'] = self.gmt_modified - if self.gmt_started is not None: - result['GmtStarted'] = self.gmt_started - if self.machine_group_id is not None: - result['MachineGroupID'] = self.machine_group_id - if self.order_id is not None: - result['OrderID'] = self.order_id - if self.pairesource_id is not None: - result['PAIResourceID'] = self.pairesource_id - if self.pay_type is not None: - result['PayType'] = self.pay_type - if self.pricing_cycle is not None: - result['PricingCycle'] = self.pricing_cycle - if self.region_id is not None: - result['RegionID'] = self.region_id - if self.request_id is not None: + if self.end_time is not None: + result['EndTime'] = self.end_time + if self.order is not None: + result['Order'] = self.order + if self.page_number is not None: + result['PageNumber'] = self.page_number + if self.page_size is not None: + result['PageSize'] = self.page_size + if self.sort_by is not None: + result['SortBy'] = self.sort_by + if self.start_time is not None: + result['StartTime'] = self.start_time + if self.user_id is not None: + result['UserId'] = self.user_id + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('EndTime') is not None: + self.end_time = m.get('EndTime') + if m.get('Order') is not None: + self.order = m.get('Order') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + if m.get('SortBy') is not None: + self.sort_by = m.get('SortBy') + if m.get('StartTime') is not None: + self.start_time = m.get('StartTime') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') + return self + + +class GetQuotaRangeUserViewMetricsResponseBody(TeaModel): + def __init__( + self, + quota_id: str = None, + request_id: str = None, + summary: QuotaUserViewMetric = None, + total_count: int = None, + user_metrics: List[QuotaUserViewMetric] = None, + ): + self.quota_id = quota_id + self.request_id = request_id + self.summary = summary + self.total_count = total_count + self.user_metrics = user_metrics + + def validate(self): + if self.summary: + self.summary.validate() + if self.user_metrics: + for k in self.user_metrics: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.quota_id is not None: + result['QuotaId'] = self.quota_id + if self.request_id is not None: result['RequestId'] = self.request_id - if self.status is not None: - result['Status'] = self.status - if self.supported_drivers is not None: - result['SupportedDrivers'] = self.supported_drivers + if self.summary is not None: + result['Summary'] = self.summary.to_map() + if self.total_count is not None: + result['TotalCount'] = self.total_count + result['UserMetrics'] = [] + if self.user_metrics is not None: + for k in self.user_metrics: + result['UserMetrics'].append(k.to_map() if k else None) return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Count') is not None: - self.count = m.get('Count') - if m.get('DefaultDriver') is not None: - self.default_driver = m.get('DefaultDriver') - if m.get('Duration') is not None: - self.duration = m.get('Duration') - if m.get('EcsType') is not None: - self.ecs_type = m.get('EcsType') - if m.get('GmtCreated') is not None: - self.gmt_created = m.get('GmtCreated') - if m.get('GmtExpired') is not None: - self.gmt_expired = m.get('GmtExpired') - if m.get('GmtModified') is not None: - self.gmt_modified = m.get('GmtModified') - if m.get('GmtStarted') is not None: - self.gmt_started = m.get('GmtStarted') - if m.get('MachineGroupID') is not None: - self.machine_group_id = m.get('MachineGroupID') - if m.get('OrderID') is not None: - self.order_id = m.get('OrderID') - if m.get('PAIResourceID') is not None: - self.pairesource_id = m.get('PAIResourceID') - if m.get('PayType') is not None: - self.pay_type = m.get('PayType') - if m.get('PricingCycle') is not None: - self.pricing_cycle = m.get('PricingCycle') - if m.get('RegionID') is not None: - self.region_id = m.get('RegionID') + if m.get('QuotaId') is not None: + self.quota_id = m.get('QuotaId') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('Status') is not None: - self.status = m.get('Status') - if m.get('SupportedDrivers') is not None: - self.supported_drivers = m.get('SupportedDrivers') + if m.get('Summary') is not None: + temp_model = QuotaUserViewMetric() + self.summary = temp_model.from_map(m['Summary']) + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') + self.user_metrics = [] + if m.get('UserMetrics') is not None: + for k in m.get('UserMetrics'): + temp_model = QuotaUserViewMetric() + self.user_metrics.append(temp_model.from_map(k)) return self -class GetMachineGroupResponse(TeaModel): +class GetQuotaRangeUserViewMetricsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetMachineGroupResponseBody = None, + body: GetQuotaRangeUserViewMetricsResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -9970,24 +10905,20 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetMachineGroupResponseBody() + temp_model = GetQuotaRangeUserViewMetricsResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetNodeMetricsRequest(TeaModel): +class GetQuotaTopoRequest(TeaModel): def __init__( self, - end_time: str = None, - gputype: str = None, - start_time: str = None, - time_step: str = None, + depth: int = None, + show_own_workloads: bool = None, verbose: bool = None, ): - self.end_time = end_time - self.gputype = gputype - self.start_time = start_time - self.time_step = time_step + self.depth = depth + self.show_own_workloads = show_own_workloads self.verbose = verbose def validate(self): @@ -9999,49 +10930,34 @@ def to_map(self): return _map result = dict() - if self.end_time is not None: - result['EndTime'] = self.end_time - if self.gputype is not None: - result['GPUType'] = self.gputype - if self.start_time is not None: - result['StartTime'] = self.start_time - if self.time_step is not None: - result['TimeStep'] = self.time_step + if self.depth is not None: + result['Depth'] = self.depth + if self.show_own_workloads is not None: + result['ShowOwnWorkloads'] = self.show_own_workloads if self.verbose is not None: result['Verbose'] = self.verbose return result def from_map(self, m: dict = None): m = m or dict() - if m.get('EndTime') is not None: - self.end_time = m.get('EndTime') - if m.get('GPUType') is not None: - self.gputype = m.get('GPUType') - if m.get('StartTime') is not None: - self.start_time = m.get('StartTime') - if m.get('TimeStep') is not None: - self.time_step = m.get('TimeStep') + if m.get('Depth') is not None: + self.depth = m.get('Depth') + if m.get('ShowOwnWorkloads') is not None: + self.show_own_workloads = m.get('ShowOwnWorkloads') if m.get('Verbose') is not None: self.verbose = m.get('Verbose') return self -class GetNodeMetricsResponseBody(TeaModel): +class GetQuotaTopoResponseBody(TeaModel): def __init__( self, - metric_type: str = None, - nodes_metrics: List[NodeMetric] = None, - resource_group_id: str = None, + request_id: str = None, ): - self.metric_type = metric_type - self.nodes_metrics = nodes_metrics - self.resource_group_id = resource_group_id + self.request_id = request_id def validate(self): - if self.nodes_metrics: - for k in self.nodes_metrics: - if k: - k.validate() + pass def to_map(self): _map = super().to_map() @@ -10049,36 +10965,23 @@ def to_map(self): return _map result = dict() - if self.metric_type is not None: - result['MetricType'] = self.metric_type - result['NodesMetrics'] = [] - if self.nodes_metrics is not None: - for k in self.nodes_metrics: - result['NodesMetrics'].append(k.to_map() if k else None) - if self.resource_group_id is not None: - result['ResourceGroupID'] = self.resource_group_id + if self.request_id is not None: + result['requestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('MetricType') is not None: - self.metric_type = m.get('MetricType') - self.nodes_metrics = [] - if m.get('NodesMetrics') is not None: - for k in m.get('NodesMetrics'): - temp_model = NodeMetric() - self.nodes_metrics.append(temp_model.from_map(k)) - if m.get('ResourceGroupID') is not None: - self.resource_group_id = m.get('ResourceGroupID') + if m.get('requestId') is not None: + self.request_id = m.get('requestId') return self -class GetNodeMetricsResponse(TeaModel): +class GetQuotaTopoResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetNodeMetricsResponseBody = None, + body: GetQuotaTopoResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -10109,24 +11012,30 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetNodeMetricsResponseBody() + temp_model = GetQuotaTopoResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetNodeViewMetricsRequest(TeaModel): +class GetQuotaUserViewMetricsRequest(TeaModel): def __init__( self, - node_id: str = None, - page_number: int = None, - page_size: int = None, + order: str = None, + page_number: str = None, + page_size: str = None, + sort_by: str = None, time_step: str = None, + user_id: str = None, workspace_id: str = None, ): - self.node_id = node_id + self.order = order + # This parameter is required. self.page_number = page_number + # This parameter is required. self.page_size = page_size + self.sort_by = sort_by self.time_step = time_step + self.user_id = user_id self.workspace_id = workspace_id def validate(self): @@ -10138,45 +11047,61 @@ def to_map(self): return _map result = dict() - if self.node_id is not None: - result['NodeId'] = self.node_id + if self.order is not None: + result['Order'] = self.order if self.page_number is not None: result['PageNumber'] = self.page_number if self.page_size is not None: result['PageSize'] = self.page_size + if self.sort_by is not None: + result['SortBy'] = self.sort_by if self.time_step is not None: result['TimeStep'] = self.time_step + if self.user_id is not None: + result['UserId'] = self.user_id if self.workspace_id is not None: result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('NodeId') is not None: - self.node_id = m.get('NodeId') + if m.get('Order') is not None: + self.order = m.get('Order') if m.get('PageNumber') is not None: self.page_number = m.get('PageNumber') if m.get('PageSize') is not None: self.page_size = m.get('PageSize') + if m.get('SortBy') is not None: + self.sort_by = m.get('SortBy') if m.get('TimeStep') is not None: self.time_step = m.get('TimeStep') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') if m.get('WorkspaceId') is not None: self.workspace_id = m.get('WorkspaceId') return self -class GetNodeViewMetricsResponseBody(TeaModel): +class GetQuotaUserViewMetricsResponseBody(TeaModel): def __init__( self, - node_metrics: List[NodeViewMetric] = None, - total: int = None, + quota_id: str = None, + request_id: str = None, + summary: QuotaUserViewMetric = None, + total_count: int = None, + user_metrics: List[QuotaUserViewMetric] = None, ): - self.node_metrics = node_metrics - self.total = total + self.quota_id = quota_id + self.request_id = request_id + self.summary = summary + self.total_count = total_count + self.user_metrics = user_metrics def validate(self): - if self.node_metrics: - for k in self.node_metrics: + if self.summary: + self.summary.validate() + if self.user_metrics: + for k in self.user_metrics: if k: k.validate() @@ -10186,32 +11111,45 @@ def to_map(self): return _map result = dict() - result['NodeMetrics'] = [] - if self.node_metrics is not None: - for k in self.node_metrics: - result['NodeMetrics'].append(k.to_map() if k else None) - if self.total is not None: - result['Total'] = self.total - return result - + if self.quota_id is not None: + result['QuotaId'] = self.quota_id + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.summary is not None: + result['Summary'] = self.summary.to_map() + if self.total_count is not None: + result['TotalCount'] = self.total_count + result['UserMetrics'] = [] + if self.user_metrics is not None: + for k in self.user_metrics: + result['UserMetrics'].append(k.to_map() if k else None) + return result + def from_map(self, m: dict = None): m = m or dict() - self.node_metrics = [] - if m.get('NodeMetrics') is not None: - for k in m.get('NodeMetrics'): - temp_model = NodeViewMetric() - self.node_metrics.append(temp_model.from_map(k)) - if m.get('Total') is not None: - self.total = m.get('Total') + if m.get('QuotaId') is not None: + self.quota_id = m.get('QuotaId') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('Summary') is not None: + temp_model = QuotaUserViewMetric() + self.summary = temp_model.from_map(m['Summary']) + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') + self.user_metrics = [] + if m.get('UserMetrics') is not None: + for k in m.get('UserMetrics'): + temp_model = QuotaUserViewMetric() + self.user_metrics.append(temp_model.from_map(k)) return self -class GetNodeViewMetricsResponse(TeaModel): +class GetQuotaUserViewMetricsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetNodeViewMetricsResponseBody = None, + body: GetQuotaUserViewMetricsResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -10242,48 +11180,98 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetNodeViewMetricsResponseBody() + temp_model = GetQuotaUserViewMetricsResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetOperationResponseBody(TeaModel): +class GetRangeUserViewMetricsRequest(TeaModel): def __init__( self, - creator_id: str = None, - gmt_created_time: str = None, - gmt_end_time: str = None, - gmt_modified_time: str = None, - gmt_start_time: str = None, - object_id: str = None, - object_type: str = None, - operation_description: str = None, - operation_id: str = None, - operation_spec_json: str = None, - operation_type: str = None, - reason_code: str = None, - reason_message: str = None, + end_time: str = None, + order: str = None, + page_number: int = None, + page_size: int = None, + sort_by: str = None, + start_time: str = None, + user_id: str = None, + workspace_id: str = None, + ): + self.end_time = end_time + self.order = order + self.page_number = page_number + self.page_size = page_size + self.sort_by = sort_by + self.start_time = start_time + self.user_id = user_id + self.workspace_id = workspace_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.end_time is not None: + result['EndTime'] = self.end_time + if self.order is not None: + result['Order'] = self.order + if self.page_number is not None: + result['PageNumber'] = self.page_number + if self.page_size is not None: + result['PageSize'] = self.page_size + if self.sort_by is not None: + result['SortBy'] = self.sort_by + if self.start_time is not None: + result['StartTime'] = self.start_time + if self.user_id is not None: + result['UserId'] = self.user_id + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('EndTime') is not None: + self.end_time = m.get('EndTime') + if m.get('Order') is not None: + self.order = m.get('Order') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + if m.get('SortBy') is not None: + self.sort_by = m.get('SortBy') + if m.get('StartTime') is not None: + self.start_time = m.get('StartTime') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') + return self + + +class GetRangeUserViewMetricsResponseBody(TeaModel): + def __init__( + self, + summary: UserViewMetric = None, + user_metrics: List[UserViewMetric] = None, request_id: str = None, - status: str = None, ): - self.creator_id = creator_id - self.gmt_created_time = gmt_created_time - self.gmt_end_time = gmt_end_time - self.gmt_modified_time = gmt_modified_time - self.gmt_start_time = gmt_start_time - self.object_id = object_id - self.object_type = object_type - self.operation_description = operation_description - self.operation_id = operation_id - self.operation_spec_json = operation_spec_json - self.operation_type = operation_type - self.reason_code = reason_code - self.reason_message = reason_message + self.summary = summary + self.user_metrics = user_metrics self.request_id = request_id - self.status = status def validate(self): - pass + if self.summary: + self.summary.validate() + if self.user_metrics: + for k in self.user_metrics: + if k: + k.validate() def to_map(self): _map = super().to_map() @@ -10291,79 +11279,37 @@ def to_map(self): return _map result = dict() - if self.creator_id is not None: - result['CreatorId'] = self.creator_id - if self.gmt_created_time is not None: - result['GmtCreatedTime'] = self.gmt_created_time - if self.gmt_end_time is not None: - result['GmtEndTime'] = self.gmt_end_time - if self.gmt_modified_time is not None: - result['GmtModifiedTime'] = self.gmt_modified_time - if self.gmt_start_time is not None: - result['GmtStartTime'] = self.gmt_start_time - if self.object_id is not None: - result['ObjectId'] = self.object_id - if self.object_type is not None: - result['ObjectType'] = self.object_type - if self.operation_description is not None: - result['OperationDescription'] = self.operation_description - if self.operation_id is not None: - result['OperationId'] = self.operation_id - if self.operation_spec_json is not None: - result['OperationSpecJson'] = self.operation_spec_json - if self.operation_type is not None: - result['OperationType'] = self.operation_type - if self.reason_code is not None: - result['ReasonCode'] = self.reason_code - if self.reason_message is not None: - result['ReasonMessage'] = self.reason_message + if self.summary is not None: + result['Summary'] = self.summary.to_map() + result['UserMetrics'] = [] + if self.user_metrics is not None: + for k in self.user_metrics: + result['UserMetrics'].append(k.to_map() if k else None) if self.request_id is not None: - result['RequestId'] = self.request_id - if self.status is not None: - result['Status'] = self.status + result['requestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('CreatorId') is not None: - self.creator_id = m.get('CreatorId') - if m.get('GmtCreatedTime') is not None: - self.gmt_created_time = m.get('GmtCreatedTime') - if m.get('GmtEndTime') is not None: - self.gmt_end_time = m.get('GmtEndTime') - if m.get('GmtModifiedTime') is not None: - self.gmt_modified_time = m.get('GmtModifiedTime') - if m.get('GmtStartTime') is not None: - self.gmt_start_time = m.get('GmtStartTime') - if m.get('ObjectId') is not None: - self.object_id = m.get('ObjectId') - if m.get('ObjectType') is not None: - self.object_type = m.get('ObjectType') - if m.get('OperationDescription') is not None: - self.operation_description = m.get('OperationDescription') - if m.get('OperationId') is not None: - self.operation_id = m.get('OperationId') - if m.get('OperationSpecJson') is not None: - self.operation_spec_json = m.get('OperationSpecJson') - if m.get('OperationType') is not None: - self.operation_type = m.get('OperationType') - if m.get('ReasonCode') is not None: - self.reason_code = m.get('ReasonCode') - if m.get('ReasonMessage') is not None: - self.reason_message = m.get('ReasonMessage') - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - if m.get('Status') is not None: - self.status = m.get('Status') + if m.get('Summary') is not None: + temp_model = UserViewMetric() + self.summary = temp_model.from_map(m['Summary']) + self.user_metrics = [] + if m.get('UserMetrics') is not None: + for k in m.get('UserMetrics'): + temp_model = UserViewMetric() + self.user_metrics.append(temp_model.from_map(k)) + if m.get('requestId') is not None: + self.request_id = m.get('requestId') return self -class GetOperationResponse(TeaModel): +class GetRangeUserViewMetricsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetOperationResponseBody = None, + body: GetRangeUserViewMetricsResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -10394,31 +11340,19 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetOperationResponseBody() + temp_model = GetRangeUserViewMetricsResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetQueueInfosRequest(TeaModel): +class GetResourceGroupRequestTag(TeaModel): def __init__( self, - order: str = None, - page_number: int = None, - page_size: int = None, - quota_ids: str = None, - sort_by: str = None, - workload_ids: str = None, - workload_type: str = None, - workspace_ids: str = None, + key: str = None, + value: str = None, ): - self.order = order - self.page_number = page_number - self.page_size = page_size - self.quota_ids = quota_ids - self.sort_by = sort_by - self.workload_ids = workload_ids - self.workload_type = workload_type - self.workspace_ids = workspace_ids + self.key = key + self.value = value def validate(self): pass @@ -10429,59 +11363,33 @@ def to_map(self): return _map result = dict() - if self.order is not None: - result['Order'] = self.order - if self.page_number is not None: - result['PageNumber'] = self.page_number - if self.page_size is not None: - result['PageSize'] = self.page_size - if self.quota_ids is not None: - result['QuotaIds'] = self.quota_ids - if self.sort_by is not None: - result['SortBy'] = self.sort_by - if self.workload_ids is not None: - result['WorkloadIds'] = self.workload_ids - if self.workload_type is not None: - result['WorkloadType'] = self.workload_type - if self.workspace_ids is not None: - result['WorkspaceIds'] = self.workspace_ids + if self.key is not None: + result['Key'] = self.key + if self.value is not None: + result['Value'] = self.value return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Order') is not None: - self.order = m.get('Order') - if m.get('PageNumber') is not None: - self.page_number = m.get('PageNumber') - if m.get('PageSize') is not None: - self.page_size = m.get('PageSize') - if m.get('QuotaIds') is not None: - self.quota_ids = m.get('QuotaIds') - if m.get('SortBy') is not None: - self.sort_by = m.get('SortBy') - if m.get('WorkloadIds') is not None: - self.workload_ids = m.get('WorkloadIds') - if m.get('WorkloadType') is not None: - self.workload_type = m.get('WorkloadType') - if m.get('WorkspaceIds') is not None: - self.workspace_ids = m.get('WorkspaceIds') + if m.get('Key') is not None: + self.key = m.get('Key') + if m.get('Value') is not None: + self.value = m.get('Value') return self -class GetQueueInfosResponseBody(TeaModel): +class GetResourceGroupRequest(TeaModel): def __init__( self, - queue_infos: List[QueueInfo] = None, - request_id: str = None, - total_count: int = None, + is_aiworkspace_data_enabled: bool = None, + tag: List[GetResourceGroupRequestTag] = None, ): - self.queue_infos = queue_infos - self.request_id = request_id - self.total_count = total_count + self.is_aiworkspace_data_enabled = is_aiworkspace_data_enabled + self.tag = tag def validate(self): - if self.queue_infos: - for k in self.queue_infos: + if self.tag: + for k in self.tag: if k: k.validate() @@ -10491,44 +11399,37 @@ def to_map(self): return _map result = dict() - result['QueueInfos'] = [] - if self.queue_infos is not None: - for k in self.queue_infos: - result['QueueInfos'].append(k.to_map() if k else None) - if self.request_id is not None: - result['RequestId'] = self.request_id - if self.total_count is not None: - result['TotalCount'] = self.total_count + if self.is_aiworkspace_data_enabled is not None: + result['IsAIWorkspaceDataEnabled'] = self.is_aiworkspace_data_enabled + result['Tag'] = [] + if self.tag is not None: + for k in self.tag: + result['Tag'].append(k.to_map() if k else None) return result def from_map(self, m: dict = None): m = m or dict() - self.queue_infos = [] - if m.get('QueueInfos') is not None: - for k in m.get('QueueInfos'): - temp_model = QueueInfo() - self.queue_infos.append(temp_model.from_map(k)) - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - if m.get('TotalCount') is not None: - self.total_count = m.get('TotalCount') + if m.get('IsAIWorkspaceDataEnabled') is not None: + self.is_aiworkspace_data_enabled = m.get('IsAIWorkspaceDataEnabled') + self.tag = [] + if m.get('Tag') is not None: + for k in m.get('Tag'): + temp_model = GetResourceGroupRequestTag() + self.tag.append(temp_model.from_map(k)) return self -class GetQueueInfosResponse(TeaModel): +class GetResourceGroupShrinkRequest(TeaModel): def __init__( self, - headers: Dict[str, str] = None, - status_code: int = None, - body: GetQueueInfosResponseBody = None, + is_aiworkspace_data_enabled: bool = None, + tag_shrink: str = None, ): - self.headers = headers - self.status_code = status_code - self.body = body + self.is_aiworkspace_data_enabled = is_aiworkspace_data_enabled + self.tag_shrink = tag_shrink def validate(self): - if self.body: - self.body.validate() + pass def to_map(self): _map = super().to_map() @@ -10536,95 +11437,94 @@ def to_map(self): return _map result = dict() - if self.headers is not None: - result['headers'] = self.headers - if self.status_code is not None: - result['statusCode'] = self.status_code - if self.body is not None: - result['body'] = self.body.to_map() + if self.is_aiworkspace_data_enabled is not None: + result['IsAIWorkspaceDataEnabled'] = self.is_aiworkspace_data_enabled + if self.tag_shrink is not None: + result['Tag'] = self.tag_shrink return result def from_map(self, m: dict = None): m = m or dict() - if m.get('headers') is not None: - self.headers = m.get('headers') - if m.get('statusCode') is not None: - self.status_code = m.get('statusCode') - if m.get('body') is not None: - temp_model = GetQueueInfosResponseBody() - self.body = temp_model.from_map(m['body']) + if m.get('IsAIWorkspaceDataEnabled') is not None: + self.is_aiworkspace_data_enabled = m.get('IsAIWorkspaceDataEnabled') + if m.get('Tag') is not None: + self.tag_shrink = m.get('Tag') return self -class GetQuotaResponseBody(TeaModel): +class GetResourceGroupResponseBodyTags(TeaModel): def __init__( self, - allocate_strategy: str = None, + tag_key: str = None, + tag_value: str = None, + ): + self.tag_key = tag_key + self.tag_value = tag_value + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.tag_key is not None: + result['TagKey'] = self.tag_key + if self.tag_value is not None: + result['TagValue'] = self.tag_value + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('TagKey') is not None: + self.tag_key = m.get('TagKey') + if m.get('TagValue') is not None: + self.tag_value = m.get('TagValue') + return self + + +class GetResourceGroupResponseBody(TeaModel): + def __init__( + self, + cluster_id: str = None, + computing_resource_provider: str = None, creator_id: str = None, description: str = None, gmt_created_time: str = None, gmt_modified_time: str = None, - labels: List[Label] = None, - latest_operation_id: str = None, - min: ResourceSpec = None, - parent_quota_id: str = None, - queue_strategy: str = None, - quota_config: QuotaConfig = None, - quota_details: QuotaDetails = None, - quota_id: str = None, - quota_name: str = None, - reason_code: str = None, - reason_message: str = None, + name: str = None, request_id: str = None, - resource_group_ids: List[str] = None, resource_type: str = None, status: str = None, - sub_quotas: List[QuotaIdName] = None, - workspaces: List[WorkspaceIdName] = None, + support_rdma: bool = None, + tags: List[GetResourceGroupResponseBodyTags] = None, + user_vpc: UserVpc = None, + workspace_id: str = None, ): - self.allocate_strategy = allocate_strategy + self.cluster_id = cluster_id + self.computing_resource_provider = computing_resource_provider self.creator_id = creator_id self.description = description self.gmt_created_time = gmt_created_time self.gmt_modified_time = gmt_modified_time - self.labels = labels - self.latest_operation_id = latest_operation_id - self.min = min - self.parent_quota_id = parent_quota_id - self.queue_strategy = queue_strategy - self.quota_config = quota_config - self.quota_details = quota_details - # Quota Id - self.quota_id = quota_id - self.quota_name = quota_name - self.reason_code = reason_code - self.reason_message = reason_message + self.name = name self.request_id = request_id - self.resource_group_ids = resource_group_ids self.resource_type = resource_type self.status = status - self.sub_quotas = sub_quotas - self.workspaces = workspaces + self.support_rdma = support_rdma + self.tags = tags + self.user_vpc = user_vpc + self.workspace_id = workspace_id def validate(self): - if self.labels: - for k in self.labels: - if k: - k.validate() - if self.min: - self.min.validate() - if self.quota_config: - self.quota_config.validate() - if self.quota_details: - self.quota_details.validate() - if self.sub_quotas: - for k in self.sub_quotas: - if k: - k.validate() - if self.workspaces: - for k in self.workspaces: + if self.tags: + for k in self.tags: if k: k.validate() + if self.user_vpc: + self.user_vpc.validate() def to_map(self): _map = super().to_map() @@ -10632,125 +11532,81 @@ def to_map(self): return _map result = dict() - if self.allocate_strategy is not None: - result['AllocateStrategy'] = self.allocate_strategy + if self.cluster_id is not None: + result['ClusterID'] = self.cluster_id + if self.computing_resource_provider is not None: + result['ComputingResourceProvider'] = self.computing_resource_provider if self.creator_id is not None: - result['CreatorId'] = self.creator_id + result['CreatorID'] = self.creator_id if self.description is not None: result['Description'] = self.description if self.gmt_created_time is not None: result['GmtCreatedTime'] = self.gmt_created_time if self.gmt_modified_time is not None: result['GmtModifiedTime'] = self.gmt_modified_time - result['Labels'] = [] - if self.labels is not None: - for k in self.labels: - result['Labels'].append(k.to_map() if k else None) - if self.latest_operation_id is not None: - result['LatestOperationId'] = self.latest_operation_id - if self.min is not None: - result['Min'] = self.min.to_map() - if self.parent_quota_id is not None: - result['ParentQuotaId'] = self.parent_quota_id - if self.queue_strategy is not None: - result['QueueStrategy'] = self.queue_strategy - if self.quota_config is not None: - result['QuotaConfig'] = self.quota_config.to_map() - if self.quota_details is not None: - result['QuotaDetails'] = self.quota_details.to_map() - if self.quota_id is not None: - result['QuotaId'] = self.quota_id - if self.quota_name is not None: - result['QuotaName'] = self.quota_name - if self.reason_code is not None: - result['ReasonCode'] = self.reason_code - if self.reason_message is not None: - result['ReasonMessage'] = self.reason_message + if self.name is not None: + result['Name'] = self.name if self.request_id is not None: result['RequestId'] = self.request_id - if self.resource_group_ids is not None: - result['ResourceGroupIds'] = self.resource_group_ids if self.resource_type is not None: result['ResourceType'] = self.resource_type if self.status is not None: result['Status'] = self.status - result['SubQuotas'] = [] - if self.sub_quotas is not None: - for k in self.sub_quotas: - result['SubQuotas'].append(k.to_map() if k else None) - result['Workspaces'] = [] - if self.workspaces is not None: - for k in self.workspaces: - result['Workspaces'].append(k.to_map() if k else None) + if self.support_rdma is not None: + result['SupportRDMA'] = self.support_rdma + result['Tags'] = [] + if self.tags is not None: + for k in self.tags: + result['Tags'].append(k.to_map() if k else None) + if self.user_vpc is not None: + result['UserVpc'] = self.user_vpc.to_map() + if self.workspace_id is not None: + result['WorkspaceID'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('AllocateStrategy') is not None: - self.allocate_strategy = m.get('AllocateStrategy') - if m.get('CreatorId') is not None: - self.creator_id = m.get('CreatorId') + if m.get('ClusterID') is not None: + self.cluster_id = m.get('ClusterID') + if m.get('ComputingResourceProvider') is not None: + self.computing_resource_provider = m.get('ComputingResourceProvider') + if m.get('CreatorID') is not None: + self.creator_id = m.get('CreatorID') if m.get('Description') is not None: self.description = m.get('Description') if m.get('GmtCreatedTime') is not None: self.gmt_created_time = m.get('GmtCreatedTime') if m.get('GmtModifiedTime') is not None: self.gmt_modified_time = m.get('GmtModifiedTime') - self.labels = [] - if m.get('Labels') is not None: - for k in m.get('Labels'): - temp_model = Label() - self.labels.append(temp_model.from_map(k)) - if m.get('LatestOperationId') is not None: - self.latest_operation_id = m.get('LatestOperationId') - if m.get('Min') is not None: - temp_model = ResourceSpec() - self.min = temp_model.from_map(m['Min']) - if m.get('ParentQuotaId') is not None: - self.parent_quota_id = m.get('ParentQuotaId') - if m.get('QueueStrategy') is not None: - self.queue_strategy = m.get('QueueStrategy') - if m.get('QuotaConfig') is not None: - temp_model = QuotaConfig() - self.quota_config = temp_model.from_map(m['QuotaConfig']) - if m.get('QuotaDetails') is not None: - temp_model = QuotaDetails() - self.quota_details = temp_model.from_map(m['QuotaDetails']) - if m.get('QuotaId') is not None: - self.quota_id = m.get('QuotaId') - if m.get('QuotaName') is not None: - self.quota_name = m.get('QuotaName') - if m.get('ReasonCode') is not None: - self.reason_code = m.get('ReasonCode') - if m.get('ReasonMessage') is not None: - self.reason_message = m.get('ReasonMessage') + if m.get('Name') is not None: + self.name = m.get('Name') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('ResourceGroupIds') is not None: - self.resource_group_ids = m.get('ResourceGroupIds') if m.get('ResourceType') is not None: self.resource_type = m.get('ResourceType') if m.get('Status') is not None: self.status = m.get('Status') - self.sub_quotas = [] - if m.get('SubQuotas') is not None: - for k in m.get('SubQuotas'): - temp_model = QuotaIdName() - self.sub_quotas.append(temp_model.from_map(k)) - self.workspaces = [] - if m.get('Workspaces') is not None: - for k in m.get('Workspaces'): - temp_model = WorkspaceIdName() - self.workspaces.append(temp_model.from_map(k)) + if m.get('SupportRDMA') is not None: + self.support_rdma = m.get('SupportRDMA') + self.tags = [] + if m.get('Tags') is not None: + for k in m.get('Tags'): + temp_model = GetResourceGroupResponseBodyTags() + self.tags.append(temp_model.from_map(k)) + if m.get('UserVpc') is not None: + temp_model = UserVpc() + self.user_vpc = temp_model.from_map(m['UserVpc']) + if m.get('WorkspaceID') is not None: + self.workspace_id = m.get('WorkspaceID') return self -class GetQuotaResponse(TeaModel): +class GetResourceGroupResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetQuotaResponseBody = None, + body: GetResourceGroupResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -10781,31 +11637,19 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetQuotaResponseBody() + temp_model = GetResourceGroupResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetQuotaJobViewMetricsRequest(TeaModel): +class GetResourceGroupMachineGroupRequestTag(TeaModel): def __init__( self, - end_time: str = None, - order: str = None, - page_number: int = None, - page_size: int = None, - sort_by: str = None, - start_time: str = None, - time_step: str = None, - workspace_id: str = None, + key: str = None, + value: str = None, ): - self.end_time = end_time - self.order = order - self.page_number = page_number - self.page_size = page_size - self.sort_by = sort_by - self.start_time = start_time - self.time_step = time_step - self.workspace_id = workspace_id + self.key = key + self.value = value def validate(self): pass @@ -10816,67 +11660,33 @@ def to_map(self): return _map result = dict() - if self.end_time is not None: - result['EndTime'] = self.end_time - if self.order is not None: - result['Order'] = self.order - if self.page_number is not None: - result['PageNumber'] = self.page_number - if self.page_size is not None: - result['PageSize'] = self.page_size - if self.sort_by is not None: - result['SortBy'] = self.sort_by - if self.start_time is not None: - result['StartTime'] = self.start_time - if self.time_step is not None: - result['TimeStep'] = self.time_step - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id + if self.key is not None: + result['Key'] = self.key + if self.value is not None: + result['Value'] = self.value return result def from_map(self, m: dict = None): m = m or dict() - if m.get('EndTime') is not None: - self.end_time = m.get('EndTime') - if m.get('Order') is not None: - self.order = m.get('Order') - if m.get('PageNumber') is not None: - self.page_number = m.get('PageNumber') - if m.get('PageSize') is not None: - self.page_size = m.get('PageSize') - if m.get('SortBy') is not None: - self.sort_by = m.get('SortBy') - if m.get('StartTime') is not None: - self.start_time = m.get('StartTime') - if m.get('TimeStep') is not None: - self.time_step = m.get('TimeStep') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') + if m.get('Key') is not None: + self.key = m.get('Key') + if m.get('Value') is not None: + self.value = m.get('Value') return self -class GetQuotaJobViewMetricsResponseBody(TeaModel): +class GetResourceGroupMachineGroupRequest(TeaModel): def __init__( self, - job_metrics: List[QuotaJobViewMetric] = None, - quota_id: str = None, - request_id: str = None, - summary: QuotaJobViewMetric = None, - total_count: int = None, + tag: List[GetResourceGroupMachineGroupRequestTag] = None, ): - self.job_metrics = job_metrics - self.quota_id = quota_id - self.request_id = request_id - self.summary = summary - self.total_count = total_count + self.tag = tag def validate(self): - if self.job_metrics: - for k in self.job_metrics: + if self.tag: + for k in self.tag: if k: k.validate() - if self.summary: - self.summary.validate() def to_map(self): _map = super().to_map() @@ -10884,53 +11694,31 @@ def to_map(self): return _map result = dict() - result['JobMetrics'] = [] - if self.job_metrics is not None: - for k in self.job_metrics: - result['JobMetrics'].append(k.to_map() if k else None) - if self.quota_id is not None: - result['QuotaId'] = self.quota_id - if self.request_id is not None: - result['RequestId'] = self.request_id - if self.summary is not None: - result['Summary'] = self.summary.to_map() - if self.total_count is not None: - result['TotalCount'] = self.total_count + result['Tag'] = [] + if self.tag is not None: + for k in self.tag: + result['Tag'].append(k.to_map() if k else None) return result def from_map(self, m: dict = None): m = m or dict() - self.job_metrics = [] - if m.get('JobMetrics') is not None: - for k in m.get('JobMetrics'): - temp_model = QuotaJobViewMetric() - self.job_metrics.append(temp_model.from_map(k)) - if m.get('QuotaId') is not None: - self.quota_id = m.get('QuotaId') - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - if m.get('Summary') is not None: - temp_model = QuotaJobViewMetric() - self.summary = temp_model.from_map(m['Summary']) - if m.get('TotalCount') is not None: - self.total_count = m.get('TotalCount') + self.tag = [] + if m.get('Tag') is not None: + for k in m.get('Tag'): + temp_model = GetResourceGroupMachineGroupRequestTag() + self.tag.append(temp_model.from_map(k)) return self -class GetQuotaJobViewMetricsResponse(TeaModel): +class GetResourceGroupMachineGroupShrinkRequest(TeaModel): def __init__( self, - headers: Dict[str, str] = None, - status_code: int = None, - body: GetQuotaJobViewMetricsResponseBody = None, + tag_shrink: str = None, ): - self.headers = headers - self.status_code = status_code - self.body = body + self.tag_shrink = tag_shrink def validate(self): - if self.body: - self.body.validate() + pass def to_map(self): _map = super().to_map() @@ -10938,38 +11726,25 @@ def to_map(self): return _map result = dict() - if self.headers is not None: - result['headers'] = self.headers - if self.status_code is not None: - result['statusCode'] = self.status_code - if self.body is not None: - result['body'] = self.body.to_map() + if self.tag_shrink is not None: + result['Tag'] = self.tag_shrink return result def from_map(self, m: dict = None): m = m or dict() - if m.get('headers') is not None: - self.headers = m.get('headers') - if m.get('statusCode') is not None: - self.status_code = m.get('statusCode') - if m.get('body') is not None: - temp_model = GetQuotaJobViewMetricsResponseBody() - self.body = temp_model.from_map(m['body']) + if m.get('Tag') is not None: + self.tag_shrink = m.get('Tag') return self -class GetQuotaMetricsRequest(TeaModel): +class GetResourceGroupMachineGroupResponseBodyTags(TeaModel): def __init__( self, - end_time: str = None, - gputype: str = None, - start_time: str = None, - time_step: str = None, + tag_key: str = None, + tag_value: str = None, ): - self.end_time = end_time - self.gputype = gputype - self.start_time = start_time - self.time_step = time_step + self.tag_key = tag_key + self.tag_value = tag_value def validate(self): pass @@ -10980,43 +11755,71 @@ def to_map(self): return _map result = dict() - if self.end_time is not None: - result['EndTime'] = self.end_time - if self.gputype is not None: - result['GPUType'] = self.gputype - if self.start_time is not None: - result['StartTime'] = self.start_time - if self.time_step is not None: - result['TimeStep'] = self.time_step + if self.tag_key is not None: + result['TagKey'] = self.tag_key + if self.tag_value is not None: + result['TagValue'] = self.tag_value return result def from_map(self, m: dict = None): m = m or dict() - if m.get('EndTime') is not None: - self.end_time = m.get('EndTime') - if m.get('GPUType') is not None: - self.gputype = m.get('GPUType') - if m.get('StartTime') is not None: - self.start_time = m.get('StartTime') - if m.get('TimeStep') is not None: - self.time_step = m.get('TimeStep') + if m.get('TagKey') is not None: + self.tag_key = m.get('TagKey') + if m.get('TagValue') is not None: + self.tag_value = m.get('TagValue') return self -class GetQuotaMetricsResponseBody(TeaModel): +class GetResourceGroupMachineGroupResponseBody(TeaModel): def __init__( self, - quota_id: str = None, - quota_metrics: List[QuotaMetric] = None, + cpu: str = None, + default_driver: str = None, + ecs_count: int = None, + ecs_spec: str = None, + gmt_created_time: str = None, + gmt_expired_time: str = None, + gmt_modified_time: str = None, + gmt_started_time: str = None, + gpu: str = None, + gpu_type: str = None, + machine_group_id: str = None, + memory: str = None, + name: str = None, + payment_duration: str = None, + payment_duration_unit: str = None, + payment_type: str = None, request_id: str = None, + resource_group_id: str = None, + status: str = None, + supported_drivers: List[str] = None, + tags: List[GetResourceGroupMachineGroupResponseBodyTags] = None, ): - self.quota_id = quota_id - self.quota_metrics = quota_metrics + self.cpu = cpu + self.default_driver = default_driver + self.ecs_count = ecs_count + self.ecs_spec = ecs_spec + self.gmt_created_time = gmt_created_time + self.gmt_expired_time = gmt_expired_time + self.gmt_modified_time = gmt_modified_time + self.gmt_started_time = gmt_started_time + self.gpu = gpu + self.gpu_type = gpu_type + self.machine_group_id = machine_group_id + self.memory = memory + self.name = name + self.payment_duration = payment_duration + self.payment_duration_unit = payment_duration_unit + self.payment_type = payment_type self.request_id = request_id + self.resource_group_id = resource_group_id + self.status = status + self.supported_drivers = supported_drivers + self.tags = tags def validate(self): - if self.quota_metrics: - for k in self.quota_metrics: + if self.tags: + for k in self.tags: if k: k.validate() @@ -11026,36 +11829,108 @@ def to_map(self): return _map result = dict() - if self.quota_id is not None: - result['QuotaId'] = self.quota_id - result['QuotaMetrics'] = [] - if self.quota_metrics is not None: - for k in self.quota_metrics: - result['QuotaMetrics'].append(k.to_map() if k else None) + if self.cpu is not None: + result['Cpu'] = self.cpu + if self.default_driver is not None: + result['DefaultDriver'] = self.default_driver + if self.ecs_count is not None: + result['EcsCount'] = self.ecs_count + if self.ecs_spec is not None: + result['EcsSpec'] = self.ecs_spec + if self.gmt_created_time is not None: + result['GmtCreatedTime'] = self.gmt_created_time + if self.gmt_expired_time is not None: + result['GmtExpiredTime'] = self.gmt_expired_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + if self.gmt_started_time is not None: + result['GmtStartedTime'] = self.gmt_started_time + if self.gpu is not None: + result['Gpu'] = self.gpu + if self.gpu_type is not None: + result['GpuType'] = self.gpu_type + if self.machine_group_id is not None: + result['MachineGroupID'] = self.machine_group_id + if self.memory is not None: + result['Memory'] = self.memory + if self.name is not None: + result['Name'] = self.name + if self.payment_duration is not None: + result['PaymentDuration'] = self.payment_duration + if self.payment_duration_unit is not None: + result['PaymentDurationUnit'] = self.payment_duration_unit + if self.payment_type is not None: + result['PaymentType'] = self.payment_type if self.request_id is not None: result['RequestId'] = self.request_id + if self.resource_group_id is not None: + result['ResourceGroupID'] = self.resource_group_id + if self.status is not None: + result['Status'] = self.status + if self.supported_drivers is not None: + result['SupportedDrivers'] = self.supported_drivers + result['Tags'] = [] + if self.tags is not None: + for k in self.tags: + result['Tags'].append(k.to_map() if k else None) return result def from_map(self, m: dict = None): m = m or dict() - if m.get('QuotaId') is not None: - self.quota_id = m.get('QuotaId') - self.quota_metrics = [] - if m.get('QuotaMetrics') is not None: - for k in m.get('QuotaMetrics'): - temp_model = QuotaMetric() - self.quota_metrics.append(temp_model.from_map(k)) + if m.get('Cpu') is not None: + self.cpu = m.get('Cpu') + if m.get('DefaultDriver') is not None: + self.default_driver = m.get('DefaultDriver') + if m.get('EcsCount') is not None: + self.ecs_count = m.get('EcsCount') + if m.get('EcsSpec') is not None: + self.ecs_spec = m.get('EcsSpec') + if m.get('GmtCreatedTime') is not None: + self.gmt_created_time = m.get('GmtCreatedTime') + if m.get('GmtExpiredTime') is not None: + self.gmt_expired_time = m.get('GmtExpiredTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + if m.get('GmtStartedTime') is not None: + self.gmt_started_time = m.get('GmtStartedTime') + if m.get('Gpu') is not None: + self.gpu = m.get('Gpu') + if m.get('GpuType') is not None: + self.gpu_type = m.get('GpuType') + if m.get('MachineGroupID') is not None: + self.machine_group_id = m.get('MachineGroupID') + if m.get('Memory') is not None: + self.memory = m.get('Memory') + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('PaymentDuration') is not None: + self.payment_duration = m.get('PaymentDuration') + if m.get('PaymentDurationUnit') is not None: + self.payment_duration_unit = m.get('PaymentDurationUnit') + if m.get('PaymentType') is not None: + self.payment_type = m.get('PaymentType') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') + if m.get('ResourceGroupID') is not None: + self.resource_group_id = m.get('ResourceGroupID') + if m.get('Status') is not None: + self.status = m.get('Status') + if m.get('SupportedDrivers') is not None: + self.supported_drivers = m.get('SupportedDrivers') + self.tags = [] + if m.get('Tags') is not None: + for k in m.get('Tags'): + temp_model = GetResourceGroupMachineGroupResponseBodyTags() + self.tags.append(temp_model.from_map(k)) return self -class GetQuotaMetricsResponse(TeaModel): +class GetResourceGroupMachineGroupResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetQuotaMetricsResponseBody = None, + body: GetResourceGroupMachineGroupResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -11086,25 +11961,23 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetQuotaMetricsResponseBody() + temp_model = GetResourceGroupMachineGroupResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetQuotaNodeMetricsRequest(TeaModel): +class GetResourceGroupMetricsRequest(TeaModel): def __init__( self, end_time: str = None, gputype: str = None, start_time: str = None, time_step: str = None, - verbose: bool = None, ): self.end_time = end_time self.gputype = gputype self.start_time = start_time self.time_step = time_step - self.verbose = verbose def validate(self): pass @@ -11123,8 +11996,6 @@ def to_map(self): result['StartTime'] = self.start_time if self.time_step is not None: result['TimeStep'] = self.time_step - if self.verbose is not None: - result['Verbose'] = self.verbose return result def from_map(self, m: dict = None): @@ -11137,27 +12008,23 @@ def from_map(self, m: dict = None): self.start_time = m.get('StartTime') if m.get('TimeStep') is not None: self.time_step = m.get('TimeStep') - if m.get('Verbose') is not None: - self.verbose = m.get('Verbose') return self -class GetQuotaNodeMetricsResponseBody(TeaModel): +class GetResourceGroupMetricsResponseBody(TeaModel): def __init__( self, - metric_type: str = None, - nodes_metrics: List[NodeMetric] = None, - quota_id: str = None, request_id: str = None, + resource_group_id: str = None, + resource_group_metrics: List[ResourceGroupMetric] = None, ): - self.metric_type = metric_type - self.nodes_metrics = nodes_metrics - self.quota_id = quota_id self.request_id = request_id + self.resource_group_id = resource_group_id + self.resource_group_metrics = resource_group_metrics def validate(self): - if self.nodes_metrics: - for k in self.nodes_metrics: + if self.resource_group_metrics: + for k in self.resource_group_metrics: if k: k.validate() @@ -11167,40 +12034,36 @@ def to_map(self): return _map result = dict() - if self.metric_type is not None: - result['MetricType'] = self.metric_type - result['NodesMetrics'] = [] - if self.nodes_metrics is not None: - for k in self.nodes_metrics: - result['NodesMetrics'].append(k.to_map() if k else None) - if self.quota_id is not None: - result['QuotaId'] = self.quota_id if self.request_id is not None: result['RequestId'] = self.request_id + if self.resource_group_id is not None: + result['ResourceGroupID'] = self.resource_group_id + result['ResourceGroupMetrics'] = [] + if self.resource_group_metrics is not None: + for k in self.resource_group_metrics: + result['ResourceGroupMetrics'].append(k.to_map() if k else None) return result def from_map(self, m: dict = None): m = m or dict() - if m.get('MetricType') is not None: - self.metric_type = m.get('MetricType') - self.nodes_metrics = [] - if m.get('NodesMetrics') is not None: - for k in m.get('NodesMetrics'): - temp_model = NodeMetric() - self.nodes_metrics.append(temp_model.from_map(k)) - if m.get('QuotaId') is not None: - self.quota_id = m.get('QuotaId') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') + if m.get('ResourceGroupID') is not None: + self.resource_group_id = m.get('ResourceGroupID') + self.resource_group_metrics = [] + if m.get('ResourceGroupMetrics') is not None: + for k in m.get('ResourceGroupMetrics'): + temp_model = ResourceGroupMetric() + self.resource_group_metrics.append(temp_model.from_map(k)) return self -class GetQuotaNodeMetricsResponse(TeaModel): +class GetResourceGroupMetricsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetQuotaNodeMetricsResponseBody = None, + body: GetResourceGroupMetricsResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -11231,37 +12094,20 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetQuotaNodeMetricsResponseBody() + temp_model = GetResourceGroupMetricsResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetQuotaNodeViewMetricsRequest(TeaModel): +class GetResourceGroupRequestRequest(TeaModel): def __init__( self, - node_id: str = None, - node_status: str = None, - order: str = None, - order_status: str = None, - page_number: int = None, - page_size: int = None, + pod_status: str = None, resource_group_id: str = None, - self_only: bool = None, - sort_by: str = None, - time_step: str = None, - workspace_id: str = None, ): - self.node_id = node_id - self.node_status = node_status - self.order = order - self.order_status = order_status - self.page_number = page_number - self.page_size = page_size + self.pod_status = pod_status + # This parameter is required. self.resource_group_id = resource_group_id - self.self_only = self_only - self.sort_by = sort_by - self.time_step = time_step - self.workspace_id = workspace_id def validate(self): pass @@ -11272,73 +12118,37 @@ def to_map(self): return _map result = dict() - if self.node_id is not None: - result['NodeId'] = self.node_id - if self.node_status is not None: - result['NodeStatus'] = self.node_status - if self.order is not None: - result['Order'] = self.order - if self.order_status is not None: - result['OrderStatus'] = self.order_status - if self.page_number is not None: - result['PageNumber'] = self.page_number - if self.page_size is not None: - result['PageSize'] = self.page_size + if self.pod_status is not None: + result['PodStatus'] = self.pod_status if self.resource_group_id is not None: - result['ResourceGroupId'] = self.resource_group_id - if self.self_only is not None: - result['SelfOnly'] = self.self_only - if self.sort_by is not None: - result['SortBy'] = self.sort_by - if self.time_step is not None: - result['TimeStep'] = self.time_step - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id + result['ResourceGroupID'] = self.resource_group_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('NodeId') is not None: - self.node_id = m.get('NodeId') - if m.get('NodeStatus') is not None: - self.node_status = m.get('NodeStatus') - if m.get('Order') is not None: - self.order = m.get('Order') - if m.get('OrderStatus') is not None: - self.order_status = m.get('OrderStatus') - if m.get('PageNumber') is not None: - self.page_number = m.get('PageNumber') - if m.get('PageSize') is not None: - self.page_size = m.get('PageSize') - if m.get('ResourceGroupId') is not None: - self.resource_group_id = m.get('ResourceGroupId') - if m.get('SelfOnly') is not None: - self.self_only = m.get('SelfOnly') - if m.get('SortBy') is not None: - self.sort_by = m.get('SortBy') - if m.get('TimeStep') is not None: - self.time_step = m.get('TimeStep') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') + if m.get('PodStatus') is not None: + self.pod_status = m.get('PodStatus') + if m.get('ResourceGroupID') is not None: + self.resource_group_id = m.get('ResourceGroupID') return self -class GetQuotaNodeViewMetricsResponseBody(TeaModel): +class GetResourceGroupRequestResponseBody(TeaModel): def __init__( self, - node_metrics: List[QuotaNodeViewMetric] = None, - quota_id: str = None, - request_id: str = None, - total_count: int = None, + request_cpu: int = None, + request_gpu: int = None, + request_gpuinfos: List[GPUInfo] = None, + request_memory: int = None, ): - self.node_metrics = node_metrics - self.quota_id = quota_id - self.request_id = request_id - self.total_count = total_count + self.request_cpu = request_cpu + self.request_gpu = request_gpu + self.request_gpuinfos = request_gpuinfos + self.request_memory = request_memory def validate(self): - if self.node_metrics: - for k in self.node_metrics: + if self.request_gpuinfos: + for k in self.request_gpuinfos: if k: k.validate() @@ -11348,40 +12158,40 @@ def to_map(self): return _map result = dict() - result['NodeMetrics'] = [] - if self.node_metrics is not None: - for k in self.node_metrics: - result['NodeMetrics'].append(k.to_map() if k else None) - if self.quota_id is not None: - result['QuotaId'] = self.quota_id - if self.request_id is not None: - result['RequestId'] = self.request_id - if self.total_count is not None: - result['TotalCount'] = self.total_count + if self.request_cpu is not None: + result['requestCPU'] = self.request_cpu + if self.request_gpu is not None: + result['requestGPU'] = self.request_gpu + result['requestGPUInfos'] = [] + if self.request_gpuinfos is not None: + for k in self.request_gpuinfos: + result['requestGPUInfos'].append(k.to_map() if k else None) + if self.request_memory is not None: + result['requestMemory'] = self.request_memory return result def from_map(self, m: dict = None): m = m or dict() - self.node_metrics = [] - if m.get('NodeMetrics') is not None: - for k in m.get('NodeMetrics'): - temp_model = QuotaNodeViewMetric() - self.node_metrics.append(temp_model.from_map(k)) - if m.get('QuotaId') is not None: - self.quota_id = m.get('QuotaId') - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - if m.get('TotalCount') is not None: - self.total_count = m.get('TotalCount') + if m.get('requestCPU') is not None: + self.request_cpu = m.get('requestCPU') + if m.get('requestGPU') is not None: + self.request_gpu = m.get('requestGPU') + self.request_gpuinfos = [] + if m.get('requestGPUInfos') is not None: + for k in m.get('requestGPUInfos'): + temp_model = GPUInfo() + self.request_gpuinfos.append(temp_model.from_map(k)) + if m.get('requestMemory') is not None: + self.request_memory = m.get('requestMemory') return self -class GetQuotaNodeViewMetricsResponse(TeaModel): +class GetResourceGroupRequestResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetQuotaNodeViewMetricsResponseBody = None, + body: GetResourceGroupRequestResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -11412,37 +12222,17 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetQuotaNodeViewMetricsResponseBody() + temp_model = GetResourceGroupRequestResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetQuotaQueueInfoRequest(TeaModel): +class GetResourceGroupTotalRequest(TeaModel): def __init__( self, - before_workload_id: str = None, - order: str = None, - page_number: int = None, - page_size: int = None, - show_own: bool = None, - sort_by: str = None, - status: str = None, - sub_quota_ids: str = None, - workload_ids: str = None, - workload_type: str = None, - workspace_ids: str = None, + resource_group_id: str = None, ): - self.before_workload_id = before_workload_id - self.order = order - self.page_number = page_number - self.page_size = page_size - self.show_own = show_own - self.sort_by = sort_by - self.status = status - self.sub_quota_ids = sub_quota_ids - self.workload_ids = workload_ids - self.workload_type = workload_type - self.workspace_ids = workspace_ids + self.resource_group_id = resource_group_id def validate(self): pass @@ -11453,71 +12243,33 @@ def to_map(self): return _map result = dict() - if self.before_workload_id is not None: - result['BeforeWorkloadId'] = self.before_workload_id - if self.order is not None: - result['Order'] = self.order - if self.page_number is not None: - result['PageNumber'] = self.page_number - if self.page_size is not None: - result['PageSize'] = self.page_size - if self.show_own is not None: - result['ShowOwn'] = self.show_own - if self.sort_by is not None: - result['SortBy'] = self.sort_by - if self.status is not None: - result['Status'] = self.status - if self.sub_quota_ids is not None: - result['SubQuotaIds'] = self.sub_quota_ids - if self.workload_ids is not None: - result['WorkloadIds'] = self.workload_ids - if self.workload_type is not None: - result['WorkloadType'] = self.workload_type - if self.workspace_ids is not None: - result['WorkspaceIds'] = self.workspace_ids + if self.resource_group_id is not None: + result['ResourceGroupID'] = self.resource_group_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('BeforeWorkloadId') is not None: - self.before_workload_id = m.get('BeforeWorkloadId') - if m.get('Order') is not None: - self.order = m.get('Order') - if m.get('PageNumber') is not None: - self.page_number = m.get('PageNumber') - if m.get('PageSize') is not None: - self.page_size = m.get('PageSize') - if m.get('ShowOwn') is not None: - self.show_own = m.get('ShowOwn') - if m.get('SortBy') is not None: - self.sort_by = m.get('SortBy') - if m.get('Status') is not None: - self.status = m.get('Status') - if m.get('SubQuotaIds') is not None: - self.sub_quota_ids = m.get('SubQuotaIds') - if m.get('WorkloadIds') is not None: - self.workload_ids = m.get('WorkloadIds') - if m.get('WorkloadType') is not None: - self.workload_type = m.get('WorkloadType') - if m.get('WorkspaceIds') is not None: - self.workspace_ids = m.get('WorkspaceIds') + if m.get('ResourceGroupID') is not None: + self.resource_group_id = m.get('ResourceGroupID') return self -class GetQuotaQueueInfoResponseBody(TeaModel): +class GetResourceGroupTotalResponseBody(TeaModel): def __init__( self, - queue_infos: List[QueueInfo] = None, - request_id: str = None, - total_count: int = None, + total_cpu: int = None, + total_gpu: int = None, + total_gpuinfos: List[GPUInfo] = None, + total_memory: int = None, ): - self.queue_infos = queue_infos - self.request_id = request_id - self.total_count = total_count + self.total_cpu = total_cpu + self.total_gpu = total_gpu + self.total_gpuinfos = total_gpuinfos + self.total_memory = total_memory def validate(self): - if self.queue_infos: - for k in self.queue_infos: + if self.total_gpuinfos: + for k in self.total_gpuinfos: if k: k.validate() @@ -11527,36 +12279,40 @@ def to_map(self): return _map result = dict() - result['QueueInfos'] = [] - if self.queue_infos is not None: - for k in self.queue_infos: - result['QueueInfos'].append(k.to_map() if k else None) - if self.request_id is not None: - result['RequestId'] = self.request_id - if self.total_count is not None: - result['TotalCount'] = self.total_count + if self.total_cpu is not None: + result['totalCPU'] = self.total_cpu + if self.total_gpu is not None: + result['totalGPU'] = self.total_gpu + result['totalGPUInfos'] = [] + if self.total_gpuinfos is not None: + for k in self.total_gpuinfos: + result['totalGPUInfos'].append(k.to_map() if k else None) + if self.total_memory is not None: + result['totalMemory'] = self.total_memory return result def from_map(self, m: dict = None): m = m or dict() - self.queue_infos = [] - if m.get('QueueInfos') is not None: - for k in m.get('QueueInfos'): - temp_model = QueueInfo() - self.queue_infos.append(temp_model.from_map(k)) - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - if m.get('TotalCount') is not None: - self.total_count = m.get('TotalCount') + if m.get('totalCPU') is not None: + self.total_cpu = m.get('totalCPU') + if m.get('totalGPU') is not None: + self.total_gpu = m.get('totalGPU') + self.total_gpuinfos = [] + if m.get('totalGPUInfos') is not None: + for k in m.get('totalGPUInfos'): + temp_model = GPUInfo() + self.total_gpuinfos.append(temp_model.from_map(k)) + if m.get('totalMemory') is not None: + self.total_memory = m.get('totalMemory') return self -class GetQuotaQueueInfoResponse(TeaModel): +class GetResourceGroupTotalResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetQuotaQueueInfoResponseBody = None, + body: GetResourceGroupTotalResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -11587,102 +12343,24 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetQuotaQueueInfoResponseBody() + temp_model = GetResourceGroupTotalResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetQuotaRangeUserViewMetricsRequest(TeaModel): - def __init__( - self, - end_time: str = None, - order: str = None, - page_number: int = None, - page_size: int = None, - sort_by: str = None, - start_time: str = None, - user_id: str = None, - workspace_id: str = None, - ): - self.end_time = end_time - self.order = order - self.page_number = page_number - self.page_size = page_size - self.sort_by = sort_by - self.start_time = start_time - self.user_id = user_id - self.workspace_id = workspace_id - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.end_time is not None: - result['EndTime'] = self.end_time - if self.order is not None: - result['Order'] = self.order - if self.page_number is not None: - result['PageNumber'] = self.page_number - if self.page_size is not None: - result['PageSize'] = self.page_size - if self.sort_by is not None: - result['SortBy'] = self.sort_by - if self.start_time is not None: - result['StartTime'] = self.start_time - if self.user_id is not None: - result['UserId'] = self.user_id - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('EndTime') is not None: - self.end_time = m.get('EndTime') - if m.get('Order') is not None: - self.order = m.get('Order') - if m.get('PageNumber') is not None: - self.page_number = m.get('PageNumber') - if m.get('PageSize') is not None: - self.page_size = m.get('PageSize') - if m.get('SortBy') is not None: - self.sort_by = m.get('SortBy') - if m.get('StartTime') is not None: - self.start_time = m.get('StartTime') - if m.get('UserId') is not None: - self.user_id = m.get('UserId') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') - return self - - -class GetQuotaRangeUserViewMetricsResponseBody(TeaModel): +class GetServiceIdentityRoleResponseBody(TeaModel): def __init__( self, - quota_id: str = None, + exist: bool = None, request_id: str = None, - summary: QuotaUserViewMetric = None, - total_count: int = None, - user_metrics: List[QuotaUserViewMetric] = None, + role_name: str = None, ): - self.quota_id = quota_id + self.exist = exist self.request_id = request_id - self.summary = summary - self.total_count = total_count - self.user_metrics = user_metrics + self.role_name = role_name def validate(self): - if self.summary: - self.summary.validate() - if self.user_metrics: - for k in self.user_metrics: - if k: - k.validate() + pass def to_map(self): _map = super().to_map() @@ -11690,45 +12368,31 @@ def to_map(self): return _map result = dict() - if self.quota_id is not None: - result['QuotaId'] = self.quota_id + if self.exist is not None: + result['Exist'] = self.exist if self.request_id is not None: result['RequestId'] = self.request_id - if self.summary is not None: - result['Summary'] = self.summary.to_map() - if self.total_count is not None: - result['TotalCount'] = self.total_count - result['UserMetrics'] = [] - if self.user_metrics is not None: - for k in self.user_metrics: - result['UserMetrics'].append(k.to_map() if k else None) + if self.role_name is not None: + result['RoleName'] = self.role_name return result def from_map(self, m: dict = None): m = m or dict() - if m.get('QuotaId') is not None: - self.quota_id = m.get('QuotaId') + if m.get('Exist') is not None: + self.exist = m.get('Exist') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('Summary') is not None: - temp_model = QuotaUserViewMetric() - self.summary = temp_model.from_map(m['Summary']) - if m.get('TotalCount') is not None: - self.total_count = m.get('TotalCount') - self.user_metrics = [] - if m.get('UserMetrics') is not None: - for k in m.get('UserMetrics'): - temp_model = QuotaUserViewMetric() - self.user_metrics.append(temp_model.from_map(k)) + if m.get('RoleName') is not None: + self.role_name = m.get('RoleName') return self -class GetQuotaRangeUserViewMetricsResponse(TeaModel): +class GetServiceIdentityRoleResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetQuotaRangeUserViewMetricsResponseBody = None, + body: GetServiceIdentityRoleResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -11759,29 +12423,27 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetQuotaRangeUserViewMetricsResponseBody() + temp_model = GetServiceIdentityRoleResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetQuotaUserViewMetricsRequest(TeaModel): +class GetSpotPriceHistoryRequest(TeaModel): def __init__( self, + end_time: str = None, order: str = None, - page_number: str = None, - page_size: str = None, + page_number: int = None, + page_size: int = None, sort_by: str = None, - time_step: str = None, - user_id: str = None, - workspace_id: str = None, + start_time: str = None, ): + self.end_time = end_time self.order = order self.page_number = page_number self.page_size = page_size self.sort_by = sort_by - self.time_step = time_step - self.user_id = user_id - self.workspace_id = workspace_id + self.start_time = start_time def validate(self): pass @@ -11792,6 +12454,8 @@ def to_map(self): return _map result = dict() + if self.end_time is not None: + result['EndTime'] = self.end_time if self.order is not None: result['Order'] = self.order if self.page_number is not None: @@ -11800,16 +12464,14 @@ def to_map(self): result['PageSize'] = self.page_size if self.sort_by is not None: result['SortBy'] = self.sort_by - if self.time_step is not None: - result['TimeStep'] = self.time_step - if self.user_id is not None: - result['UserId'] = self.user_id - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id + if self.start_time is not None: + result['StartTime'] = self.start_time return result def from_map(self, m: dict = None): m = m or dict() + if m.get('EndTime') is not None: + self.end_time = m.get('EndTime') if m.get('Order') is not None: self.order = m.get('Order') if m.get('PageNumber') is not None: @@ -11818,35 +12480,25 @@ def from_map(self, m: dict = None): self.page_size = m.get('PageSize') if m.get('SortBy') is not None: self.sort_by = m.get('SortBy') - if m.get('TimeStep') is not None: - self.time_step = m.get('TimeStep') - if m.get('UserId') is not None: - self.user_id = m.get('UserId') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') + if m.get('StartTime') is not None: + self.start_time = m.get('StartTime') return self -class GetQuotaUserViewMetricsResponseBody(TeaModel): +class GetSpotPriceHistoryResponseBody(TeaModel): def __init__( self, - quota_id: str = None, request_id: str = None, - summary: QuotaUserViewMetric = None, + spot_price_history: List[SpotPriceItem] = None, total_count: int = None, - user_metrics: List[QuotaUserViewMetric] = None, ): - self.quota_id = quota_id self.request_id = request_id - self.summary = summary + self.spot_price_history = spot_price_history self.total_count = total_count - self.user_metrics = user_metrics def validate(self): - if self.summary: - self.summary.validate() - if self.user_metrics: - for k in self.user_metrics: + if self.spot_price_history: + for k in self.spot_price_history: if k: k.validate() @@ -11856,45 +12508,36 @@ def to_map(self): return _map result = dict() - if self.quota_id is not None: - result['QuotaId'] = self.quota_id if self.request_id is not None: result['RequestId'] = self.request_id - if self.summary is not None: - result['Summary'] = self.summary.to_map() + result['SpotPriceHistory'] = [] + if self.spot_price_history is not None: + for k in self.spot_price_history: + result['SpotPriceHistory'].append(k.to_map() if k else None) if self.total_count is not None: result['TotalCount'] = self.total_count - result['UserMetrics'] = [] - if self.user_metrics is not None: - for k in self.user_metrics: - result['UserMetrics'].append(k.to_map() if k else None) return result def from_map(self, m: dict = None): m = m or dict() - if m.get('QuotaId') is not None: - self.quota_id = m.get('QuotaId') if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('Summary') is not None: - temp_model = QuotaUserViewMetric() - self.summary = temp_model.from_map(m['Summary']) + self.spot_price_history = [] + if m.get('SpotPriceHistory') is not None: + for k in m.get('SpotPriceHistory'): + temp_model = SpotPriceItem() + self.spot_price_history.append(temp_model.from_map(k)) if m.get('TotalCount') is not None: self.total_count = m.get('TotalCount') - self.user_metrics = [] - if m.get('UserMetrics') is not None: - for k in m.get('UserMetrics'): - temp_model = QuotaUserViewMetric() - self.user_metrics.append(temp_model.from_map(k)) return self -class GetQuotaUserViewMetricsResponse(TeaModel): +class GetSpotPriceHistoryResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetQuotaUserViewMetricsResponseBody = None, + body: GetSpotPriceHistoryResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -11925,31 +12568,21 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetQuotaUserViewMetricsResponseBody() + temp_model = GetSpotPriceHistoryResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetRangeUserViewMetricsRequest(TeaModel): +class GetSpotStockPreviewResponseBody(TeaModel): def __init__( self, - end_time: str = None, - order: str = None, - page_number: int = None, - page_size: int = None, - sort_by: str = None, - start_time: str = None, - user_id: str = None, - workspace_id: str = None, + instance_type: str = None, + request_id: str = None, + stock_status: str = None, ): - self.end_time = end_time - self.order = order - self.page_number = page_number - self.page_size = page_size - self.sort_by = sort_by - self.start_time = start_time - self.user_id = user_id - self.workspace_id = workspace_id + self.instance_type = instance_type + self.request_id = request_id + self.stock_status = stock_status def validate(self): pass @@ -11960,101 +12593,31 @@ def to_map(self): return _map result = dict() - if self.end_time is not None: - result['EndTime'] = self.end_time - if self.order is not None: - result['Order'] = self.order - if self.page_number is not None: - result['PageNumber'] = self.page_number - if self.page_size is not None: - result['PageSize'] = self.page_size - if self.sort_by is not None: - result['SortBy'] = self.sort_by - if self.start_time is not None: - result['StartTime'] = self.start_time - if self.user_id is not None: - result['UserId'] = self.user_id - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('EndTime') is not None: - self.end_time = m.get('EndTime') - if m.get('Order') is not None: - self.order = m.get('Order') - if m.get('PageNumber') is not None: - self.page_number = m.get('PageNumber') - if m.get('PageSize') is not None: - self.page_size = m.get('PageSize') - if m.get('SortBy') is not None: - self.sort_by = m.get('SortBy') - if m.get('StartTime') is not None: - self.start_time = m.get('StartTime') - if m.get('UserId') is not None: - self.user_id = m.get('UserId') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') - return self - - -class GetRangeUserViewMetricsResponseBody(TeaModel): - def __init__( - self, - summary: UserViewMetric = None, - user_metrics: List[UserViewMetric] = None, - request_id: str = None, - ): - self.summary = summary - self.user_metrics = user_metrics - self.request_id = request_id - - def validate(self): - if self.summary: - self.summary.validate() - if self.user_metrics: - for k in self.user_metrics: - if k: - k.validate() - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.summary is not None: - result['Summary'] = self.summary.to_map() - result['UserMetrics'] = [] - if self.user_metrics is not None: - for k in self.user_metrics: - result['UserMetrics'].append(k.to_map() if k else None) + if self.instance_type is not None: + result['InstanceType'] = self.instance_type if self.request_id is not None: - result['requestId'] = self.request_id + result['RequestId'] = self.request_id + if self.stock_status is not None: + result['StockStatus'] = self.stock_status return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Summary') is not None: - temp_model = UserViewMetric() - self.summary = temp_model.from_map(m['Summary']) - self.user_metrics = [] - if m.get('UserMetrics') is not None: - for k in m.get('UserMetrics'): - temp_model = UserViewMetric() - self.user_metrics.append(temp_model.from_map(k)) - if m.get('requestId') is not None: - self.request_id = m.get('requestId') + if m.get('InstanceType') is not None: + self.instance_type = m.get('InstanceType') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('StockStatus') is not None: + self.stock_status = m.get('StockStatus') return self -class GetRangeUserViewMetricsResponse(TeaModel): +class GetSpotStockPreviewResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetRangeUserViewMetricsResponseBody = None, + body: GetSpotStockPreviewResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -12085,19 +12648,20 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetRangeUserViewMetricsResponseBody() + temp_model = GetSpotStockPreviewResponseBody() self.body = temp_model.from_map(m['body']) return self -class GetResourceGroupRequestTag(TeaModel): +class GetTokenRequest(TeaModel): def __init__( self, - key: str = None, - value: str = None, + expire_time: int = None, + training_job_id: str = None, ): - self.key = key - self.value = value + self.expire_time = expire_time + # This parameter is required. + self.training_job_id = training_job_id def validate(self): pass @@ -12108,35 +12672,32 @@ def to_map(self): return _map result = dict() - if self.key is not None: - result['Key'] = self.key - if self.value is not None: - result['Value'] = self.value + if self.expire_time is not None: + result['ExpireTime'] = self.expire_time + if self.training_job_id is not None: + result['TrainingJobId'] = self.training_job_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Key') is not None: - self.key = m.get('Key') - if m.get('Value') is not None: - self.value = m.get('Value') + if m.get('ExpireTime') is not None: + self.expire_time = m.get('ExpireTime') + if m.get('TrainingJobId') is not None: + self.training_job_id = m.get('TrainingJobId') return self -class GetResourceGroupRequest(TeaModel): +class GetTokenResponseBody(TeaModel): def __init__( self, - is_aiworkspace_data_enabled: bool = None, - tag: List[GetResourceGroupRequestTag] = None, + request_id: str = None, + token: str = None, ): - self.is_aiworkspace_data_enabled = is_aiworkspace_data_enabled - self.tag = tag + self.request_id = request_id + self.token = token def validate(self): - if self.tag: - for k in self.tag: - if k: - k.validate() + pass def to_map(self): _map = super().to_map() @@ -12144,37 +12705,35 @@ def to_map(self): return _map result = dict() - if self.is_aiworkspace_data_enabled is not None: - result['IsAIWorkspaceDataEnabled'] = self.is_aiworkspace_data_enabled - result['Tag'] = [] - if self.tag is not None: - for k in self.tag: - result['Tag'].append(k.to_map() if k else None) + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.token is not None: + result['Token'] = self.token return result def from_map(self, m: dict = None): m = m or dict() - if m.get('IsAIWorkspaceDataEnabled') is not None: - self.is_aiworkspace_data_enabled = m.get('IsAIWorkspaceDataEnabled') - self.tag = [] - if m.get('Tag') is not None: - for k in m.get('Tag'): - temp_model = GetResourceGroupRequestTag() - self.tag.append(temp_model.from_map(k)) + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('Token') is not None: + self.token = m.get('Token') return self -class GetResourceGroupShrinkRequest(TeaModel): +class GetTokenResponse(TeaModel): def __init__( self, - is_aiworkspace_data_enabled: bool = None, - tag_shrink: str = None, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetTokenResponseBody = None, ): - self.is_aiworkspace_data_enabled = is_aiworkspace_data_enabled - self.tag_shrink = tag_shrink + self.headers = headers + self.status_code = status_code + self.body = body def validate(self): - pass + if self.body: + self.body.validate() def to_map(self): _map = super().to_map() @@ -12182,29 +12741,32 @@ def to_map(self): return _map result = dict() - if self.is_aiworkspace_data_enabled is not None: - result['IsAIWorkspaceDataEnabled'] = self.is_aiworkspace_data_enabled - if self.tag_shrink is not None: - result['Tag'] = self.tag_shrink + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() return result def from_map(self, m: dict = None): m = m or dict() - if m.get('IsAIWorkspaceDataEnabled') is not None: - self.is_aiworkspace_data_enabled = m.get('IsAIWorkspaceDataEnabled') - if m.get('Tag') is not None: - self.tag_shrink = m.get('Tag') + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetTokenResponseBody() + self.body = temp_model.from_map(m['body']) return self -class GetResourceGroupResponseBodyTags(TeaModel): +class GetTrainingJobRequest(TeaModel): def __init__( self, - tag_key: str = None, - tag_value: str = None, + token: str = None, ): - self.tag_key = tag_key - self.tag_value = tag_value + self.token = token def validate(self): pass @@ -12215,61 +12777,34 @@ def to_map(self): return _map result = dict() - if self.tag_key is not None: - result['TagKey'] = self.tag_key - if self.tag_value is not None: - result['TagValue'] = self.tag_value + if self.token is not None: + result['Token'] = self.token return result def from_map(self, m: dict = None): m = m or dict() - if m.get('TagKey') is not None: - self.tag_key = m.get('TagKey') - if m.get('TagValue') is not None: - self.tag_value = m.get('TagValue') + if m.get('Token') is not None: + self.token = m.get('Token') return self -class GetResourceGroupResponseBody(TeaModel): +class GetTrainingJobResponseBodyComputeResourceInstanceSpec(TeaModel): def __init__( self, - cluster_id: str = None, - computing_resource_provider: str = None, - creator_id: str = None, - description: str = None, - gmt_created_time: str = None, - gmt_modified_time: str = None, - name: str = None, - request_id: str = None, - resource_type: str = None, - status: str = None, - support_rdma: bool = None, - tags: List[GetResourceGroupResponseBodyTags] = None, - user_vpc: UserVpc = None, - workspace_id: str = None, + cpu: str = None, + gpu: str = None, + gputype: str = None, + memory: str = None, + shared_memory: str = None, ): - self.cluster_id = cluster_id - self.computing_resource_provider = computing_resource_provider - self.creator_id = creator_id - self.description = description - self.gmt_created_time = gmt_created_time - self.gmt_modified_time = gmt_modified_time - self.name = name - self.request_id = request_id - self.resource_type = resource_type - self.status = status - self.support_rdma = support_rdma - self.tags = tags - self.user_vpc = user_vpc - self.workspace_id = workspace_id + self.cpu = cpu + self.gpu = gpu + self.gputype = gputype + self.memory = memory + self.shared_memory = shared_memory def validate(self): - if self.tags: - for k in self.tags: - if k: - k.validate() - if self.user_vpc: - self.user_vpc.validate() + pass def to_map(self): _map = super().to_map() @@ -12277,89 +12812,44 @@ def to_map(self): return _map result = dict() - if self.cluster_id is not None: - result['ClusterID'] = self.cluster_id - if self.computing_resource_provider is not None: - result['ComputingResourceProvider'] = self.computing_resource_provider - if self.creator_id is not None: - result['CreatorID'] = self.creator_id - if self.description is not None: - result['Description'] = self.description - if self.gmt_created_time is not None: - result['GmtCreatedTime'] = self.gmt_created_time - if self.gmt_modified_time is not None: - result['GmtModifiedTime'] = self.gmt_modified_time - if self.name is not None: - result['Name'] = self.name - if self.request_id is not None: - result['RequestId'] = self.request_id - if self.resource_type is not None: - result['ResourceType'] = self.resource_type - if self.status is not None: - result['Status'] = self.status - if self.support_rdma is not None: - result['SupportRDMA'] = self.support_rdma - result['Tags'] = [] - if self.tags is not None: - for k in self.tags: - result['Tags'].append(k.to_map() if k else None) - if self.user_vpc is not None: - result['UserVpc'] = self.user_vpc.to_map() - if self.workspace_id is not None: - result['WorkspaceID'] = self.workspace_id + if self.cpu is not None: + result['CPU'] = self.cpu + if self.gpu is not None: + result['GPU'] = self.gpu + if self.gputype is not None: + result['GPUType'] = self.gputype + if self.memory is not None: + result['Memory'] = self.memory + if self.shared_memory is not None: + result['SharedMemory'] = self.shared_memory return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ClusterID') is not None: - self.cluster_id = m.get('ClusterID') - if m.get('ComputingResourceProvider') is not None: - self.computing_resource_provider = m.get('ComputingResourceProvider') - if m.get('CreatorID') is not None: - self.creator_id = m.get('CreatorID') - if m.get('Description') is not None: - self.description = m.get('Description') - if m.get('GmtCreatedTime') is not None: - self.gmt_created_time = m.get('GmtCreatedTime') - if m.get('GmtModifiedTime') is not None: - self.gmt_modified_time = m.get('GmtModifiedTime') - if m.get('Name') is not None: - self.name = m.get('Name') - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - if m.get('ResourceType') is not None: - self.resource_type = m.get('ResourceType') - if m.get('Status') is not None: - self.status = m.get('Status') - if m.get('SupportRDMA') is not None: - self.support_rdma = m.get('SupportRDMA') - self.tags = [] - if m.get('Tags') is not None: - for k in m.get('Tags'): - temp_model = GetResourceGroupResponseBodyTags() - self.tags.append(temp_model.from_map(k)) - if m.get('UserVpc') is not None: - temp_model = UserVpc() - self.user_vpc = temp_model.from_map(m['UserVpc']) - if m.get('WorkspaceID') is not None: - self.workspace_id = m.get('WorkspaceID') + if m.get('CPU') is not None: + self.cpu = m.get('CPU') + if m.get('GPU') is not None: + self.gpu = m.get('GPU') + if m.get('GPUType') is not None: + self.gputype = m.get('GPUType') + if m.get('Memory') is not None: + self.memory = m.get('Memory') + if m.get('SharedMemory') is not None: + self.shared_memory = m.get('SharedMemory') return self -class GetResourceGroupResponse(TeaModel): +class GetTrainingJobResponseBodyComputeResourceSpotSpec(TeaModel): def __init__( self, - headers: Dict[str, str] = None, - status_code: int = None, - body: GetResourceGroupResponseBody = None, + spot_discount_limit: float = None, + spot_strategy: str = None, ): - self.headers = headers - self.status_code = status_code - self.body = body + self.spot_discount_limit = spot_discount_limit + self.spot_strategy = spot_strategy def validate(self): - if self.body: - self.body.validate() + pass def to_map(self): _map = super().to_map() @@ -12367,37 +12857,45 @@ def to_map(self): return _map result = dict() - if self.headers is not None: - result['headers'] = self.headers - if self.status_code is not None: - result['statusCode'] = self.status_code - if self.body is not None: - result['body'] = self.body.to_map() + if self.spot_discount_limit is not None: + result['SpotDiscountLimit'] = self.spot_discount_limit + if self.spot_strategy is not None: + result['SpotStrategy'] = self.spot_strategy return result def from_map(self, m: dict = None): m = m or dict() - if m.get('headers') is not None: - self.headers = m.get('headers') - if m.get('statusCode') is not None: - self.status_code = m.get('statusCode') - if m.get('body') is not None: - temp_model = GetResourceGroupResponseBody() - self.body = temp_model.from_map(m['body']) + if m.get('SpotDiscountLimit') is not None: + self.spot_discount_limit = m.get('SpotDiscountLimit') + if m.get('SpotStrategy') is not None: + self.spot_strategy = m.get('SpotStrategy') return self -class GetResourceGroupMachineGroupRequestTag(TeaModel): +class GetTrainingJobResponseBodyComputeResource(TeaModel): def __init__( self, - key: str = None, - value: str = None, + ecs_count: int = None, + ecs_spec: str = None, + instance_count: int = None, + instance_spec: GetTrainingJobResponseBodyComputeResourceInstanceSpec = None, + resource_id: str = None, + spot_spec: GetTrainingJobResponseBodyComputeResourceSpotSpec = None, + use_spot_instance: bool = None, ): - self.key = key - self.value = value + self.ecs_count = ecs_count + self.ecs_spec = ecs_spec + self.instance_count = instance_count + self.instance_spec = instance_spec + self.resource_id = resource_id + self.spot_spec = spot_spec + self.use_spot_instance = use_spot_instance def validate(self): - pass + if self.instance_spec: + self.instance_spec.validate() + if self.spot_spec: + self.spot_spec.validate() def to_map(self): _map = super().to_map() @@ -12405,33 +12903,54 @@ def to_map(self): return _map result = dict() - if self.key is not None: - result['Key'] = self.key - if self.value is not None: - result['Value'] = self.value + if self.ecs_count is not None: + result['EcsCount'] = self.ecs_count + if self.ecs_spec is not None: + result['EcsSpec'] = self.ecs_spec + if self.instance_count is not None: + result['InstanceCount'] = self.instance_count + if self.instance_spec is not None: + result['InstanceSpec'] = self.instance_spec.to_map() + if self.resource_id is not None: + result['ResourceId'] = self.resource_id + if self.spot_spec is not None: + result['SpotSpec'] = self.spot_spec.to_map() + if self.use_spot_instance is not None: + result['UseSpotInstance'] = self.use_spot_instance return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Key') is not None: - self.key = m.get('Key') - if m.get('Value') is not None: - self.value = m.get('Value') + if m.get('EcsCount') is not None: + self.ecs_count = m.get('EcsCount') + if m.get('EcsSpec') is not None: + self.ecs_spec = m.get('EcsSpec') + if m.get('InstanceCount') is not None: + self.instance_count = m.get('InstanceCount') + if m.get('InstanceSpec') is not None: + temp_model = GetTrainingJobResponseBodyComputeResourceInstanceSpec() + self.instance_spec = temp_model.from_map(m['InstanceSpec']) + if m.get('ResourceId') is not None: + self.resource_id = m.get('ResourceId') + if m.get('SpotSpec') is not None: + temp_model = GetTrainingJobResponseBodyComputeResourceSpotSpec() + self.spot_spec = temp_model.from_map(m['SpotSpec']) + if m.get('UseSpotInstance') is not None: + self.use_spot_instance = m.get('UseSpotInstance') return self -class GetResourceGroupMachineGroupRequest(TeaModel): +class GetTrainingJobResponseBodyExperimentConfig(TeaModel): def __init__( self, - tag: List[GetResourceGroupMachineGroupRequestTag] = None, + experiment_id: str = None, + experiment_name: str = None, ): - self.tag = tag + self.experiment_id = experiment_id + self.experiment_name = experiment_name def validate(self): - if self.tag: - for k in self.tag: - if k: - k.validate() + pass def to_map(self): _map = super().to_map() @@ -12439,28 +12958,29 @@ def to_map(self): return _map result = dict() - result['Tag'] = [] - if self.tag is not None: - for k in self.tag: - result['Tag'].append(k.to_map() if k else None) + if self.experiment_id is not None: + result['ExperimentId'] = self.experiment_id + if self.experiment_name is not None: + result['ExperimentName'] = self.experiment_name return result def from_map(self, m: dict = None): m = m or dict() - self.tag = [] - if m.get('Tag') is not None: - for k in m.get('Tag'): - temp_model = GetResourceGroupMachineGroupRequestTag() - self.tag.append(temp_model.from_map(k)) + if m.get('ExperimentId') is not None: + self.experiment_id = m.get('ExperimentId') + if m.get('ExperimentName') is not None: + self.experiment_name = m.get('ExperimentName') return self -class GetResourceGroupMachineGroupShrinkRequest(TeaModel): +class GetTrainingJobResponseBodyHyperParameters(TeaModel): def __init__( self, - tag_shrink: str = None, + name: str = None, + value: str = None, ): - self.tag_shrink = tag_shrink + self.name = name + self.value = value def validate(self): pass @@ -12471,25 +12991,31 @@ def to_map(self): return _map result = dict() - if self.tag_shrink is not None: - result['Tag'] = self.tag_shrink + if self.name is not None: + result['Name'] = self.name + if self.value is not None: + result['Value'] = self.value return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Tag') is not None: - self.tag_shrink = m.get('Tag') + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('Value') is not None: + self.value = m.get('Value') return self -class GetResourceGroupMachineGroupResponseBodyTags(TeaModel): +class GetTrainingJobResponseBodyInputChannels(TeaModel): def __init__( self, - tag_key: str = None, - tag_value: str = None, + dataset_id: str = None, + input_uri: str = None, + name: str = None, ): - self.tag_key = tag_key - self.tag_value = tag_value + self.dataset_id = dataset_id + self.input_uri = input_uri + self.name = name def validate(self): pass @@ -12500,73 +13026,38 @@ def to_map(self): return _map result = dict() - if self.tag_key is not None: - result['TagKey'] = self.tag_key - if self.tag_value is not None: - result['TagValue'] = self.tag_value + if self.dataset_id is not None: + result['DatasetId'] = self.dataset_id + if self.input_uri is not None: + result['InputUri'] = self.input_uri + if self.name is not None: + result['Name'] = self.name return result def from_map(self, m: dict = None): m = m or dict() - if m.get('TagKey') is not None: - self.tag_key = m.get('TagKey') - if m.get('TagValue') is not None: - self.tag_value = m.get('TagValue') + if m.get('DatasetId') is not None: + self.dataset_id = m.get('DatasetId') + if m.get('InputUri') is not None: + self.input_uri = m.get('InputUri') + if m.get('Name') is not None: + self.name = m.get('Name') return self -class GetResourceGroupMachineGroupResponseBody(TeaModel): +class GetTrainingJobResponseBodyInstances(TeaModel): def __init__( self, - cpu: str = None, - default_driver: str = None, - ecs_count: int = None, - ecs_spec: str = None, - gmt_created_time: str = None, - gmt_expired_time: str = None, - gmt_modified_time: str = None, - gmt_started_time: str = None, - gpu: str = None, - gpu_type: str = None, - machine_group_id: str = None, - memory: str = None, name: str = None, - payment_duration: str = None, - payment_duration_unit: str = None, - payment_type: str = None, - request_id: str = None, - resource_group_id: str = None, + role: str = None, status: str = None, - supported_drivers: List[str] = None, - tags: List[GetResourceGroupMachineGroupResponseBodyTags] = None, ): - self.cpu = cpu - self.default_driver = default_driver - self.ecs_count = ecs_count - self.ecs_spec = ecs_spec - self.gmt_created_time = gmt_created_time - self.gmt_expired_time = gmt_expired_time - self.gmt_modified_time = gmt_modified_time - self.gmt_started_time = gmt_started_time - self.gpu = gpu - self.gpu_type = gpu_type - self.machine_group_id = machine_group_id - self.memory = memory self.name = name - self.payment_duration = payment_duration - self.payment_duration_unit = payment_duration_unit - self.payment_type = payment_type - self.request_id = request_id - self.resource_group_id = resource_group_id + self.role = role self.status = status - self.supported_drivers = supported_drivers - self.tags = tags def validate(self): - if self.tags: - for k in self.tags: - if k: - k.validate() + pass def to_map(self): _map = super().to_map() @@ -12574,116 +13065,36 @@ def to_map(self): return _map result = dict() - if self.cpu is not None: - result['Cpu'] = self.cpu - if self.default_driver is not None: - result['DefaultDriver'] = self.default_driver - if self.ecs_count is not None: - result['EcsCount'] = self.ecs_count - if self.ecs_spec is not None: - result['EcsSpec'] = self.ecs_spec - if self.gmt_created_time is not None: - result['GmtCreatedTime'] = self.gmt_created_time - if self.gmt_expired_time is not None: - result['GmtExpiredTime'] = self.gmt_expired_time - if self.gmt_modified_time is not None: - result['GmtModifiedTime'] = self.gmt_modified_time - if self.gmt_started_time is not None: - result['GmtStartedTime'] = self.gmt_started_time - if self.gpu is not None: - result['Gpu'] = self.gpu - if self.gpu_type is not None: - result['GpuType'] = self.gpu_type - if self.machine_group_id is not None: - result['MachineGroupID'] = self.machine_group_id - if self.memory is not None: - result['Memory'] = self.memory if self.name is not None: result['Name'] = self.name - if self.payment_duration is not None: - result['PaymentDuration'] = self.payment_duration - if self.payment_duration_unit is not None: - result['PaymentDurationUnit'] = self.payment_duration_unit - if self.payment_type is not None: - result['PaymentType'] = self.payment_type - if self.request_id is not None: - result['RequestId'] = self.request_id - if self.resource_group_id is not None: - result['ResourceGroupID'] = self.resource_group_id + if self.role is not None: + result['Role'] = self.role if self.status is not None: result['Status'] = self.status - if self.supported_drivers is not None: - result['SupportedDrivers'] = self.supported_drivers - result['Tags'] = [] - if self.tags is not None: - for k in self.tags: - result['Tags'].append(k.to_map() if k else None) return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Cpu') is not None: - self.cpu = m.get('Cpu') - if m.get('DefaultDriver') is not None: - self.default_driver = m.get('DefaultDriver') - if m.get('EcsCount') is not None: - self.ecs_count = m.get('EcsCount') - if m.get('EcsSpec') is not None: - self.ecs_spec = m.get('EcsSpec') - if m.get('GmtCreatedTime') is not None: - self.gmt_created_time = m.get('GmtCreatedTime') - if m.get('GmtExpiredTime') is not None: - self.gmt_expired_time = m.get('GmtExpiredTime') - if m.get('GmtModifiedTime') is not None: - self.gmt_modified_time = m.get('GmtModifiedTime') - if m.get('GmtStartedTime') is not None: - self.gmt_started_time = m.get('GmtStartedTime') - if m.get('Gpu') is not None: - self.gpu = m.get('Gpu') - if m.get('GpuType') is not None: - self.gpu_type = m.get('GpuType') - if m.get('MachineGroupID') is not None: - self.machine_group_id = m.get('MachineGroupID') - if m.get('Memory') is not None: - self.memory = m.get('Memory') if m.get('Name') is not None: self.name = m.get('Name') - if m.get('PaymentDuration') is not None: - self.payment_duration = m.get('PaymentDuration') - if m.get('PaymentDurationUnit') is not None: - self.payment_duration_unit = m.get('PaymentDurationUnit') - if m.get('PaymentType') is not None: - self.payment_type = m.get('PaymentType') - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - if m.get('ResourceGroupID') is not None: - self.resource_group_id = m.get('ResourceGroupID') + if m.get('Role') is not None: + self.role = m.get('Role') if m.get('Status') is not None: self.status = m.get('Status') - if m.get('SupportedDrivers') is not None: - self.supported_drivers = m.get('SupportedDrivers') - self.tags = [] - if m.get('Tags') is not None: - for k in m.get('Tags'): - temp_model = GetResourceGroupMachineGroupResponseBodyTags() - self.tags.append(temp_model.from_map(k)) return self -class GetResourceGroupMachineGroupResponse(TeaModel): +class GetTrainingJobResponseBodyLabels(TeaModel): def __init__( self, - headers: Dict[str, str] = None, - status_code: int = None, - body: GetResourceGroupMachineGroupResponseBody = None, + key: str = None, + value: str = None, ): - self.headers = headers - self.status_code = status_code - self.body = body + self.key = key + self.value = value def validate(self): - if self.body: - self.body.validate() + pass def to_map(self): _map = super().to_map() @@ -12691,38 +13102,31 @@ def to_map(self): return _map result = dict() - if self.headers is not None: - result['headers'] = self.headers - if self.status_code is not None: - result['statusCode'] = self.status_code - if self.body is not None: - result['body'] = self.body.to_map() + if self.key is not None: + result['Key'] = self.key + if self.value is not None: + result['Value'] = self.value return result def from_map(self, m: dict = None): m = m or dict() - if m.get('headers') is not None: - self.headers = m.get('headers') - if m.get('statusCode') is not None: - self.status_code = m.get('statusCode') - if m.get('body') is not None: - temp_model = GetResourceGroupMachineGroupResponseBody() - self.body = temp_model.from_map(m['body']) + if m.get('Key') is not None: + self.key = m.get('Key') + if m.get('Value') is not None: + self.value = m.get('Value') return self -class GetResourceGroupMetricsRequest(TeaModel): +class GetTrainingJobResponseBodyLatestMetrics(TeaModel): def __init__( self, - end_time: str = None, - gputype: str = None, - start_time: str = None, - time_step: str = None, + name: str = None, + timestamp: str = None, + value: float = None, ): - self.end_time = end_time - self.gputype = gputype - self.start_time = start_time - self.time_step = time_step + self.name = name + self.timestamp = timestamp + self.value = value def validate(self): pass @@ -12733,45 +13137,36 @@ def to_map(self): return _map result = dict() - if self.end_time is not None: - result['EndTime'] = self.end_time - if self.gputype is not None: - result['GPUType'] = self.gputype - if self.start_time is not None: - result['StartTime'] = self.start_time - if self.time_step is not None: - result['TimeStep'] = self.time_step + if self.name is not None: + result['Name'] = self.name + if self.timestamp is not None: + result['Timestamp'] = self.timestamp + if self.value is not None: + result['Value'] = self.value return result def from_map(self, m: dict = None): m = m or dict() - if m.get('EndTime') is not None: - self.end_time = m.get('EndTime') - if m.get('GPUType') is not None: - self.gputype = m.get('GPUType') - if m.get('StartTime') is not None: - self.start_time = m.get('StartTime') - if m.get('TimeStep') is not None: - self.time_step = m.get('TimeStep') + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('Timestamp') is not None: + self.timestamp = m.get('Timestamp') + if m.get('Value') is not None: + self.value = m.get('Value') return self -class GetResourceGroupMetricsResponseBody(TeaModel): +class GetTrainingJobResponseBodyLatestProgressOverallProgress(TeaModel): def __init__( self, - request_id: str = None, - resource_group_id: str = None, - resource_group_metrics: List[ResourceGroupMetric] = None, + timestamp: str = None, + value: float = None, ): - self.request_id = request_id - self.resource_group_id = resource_group_id - self.resource_group_metrics = resource_group_metrics + self.timestamp = timestamp + self.value = value def validate(self): - if self.resource_group_metrics: - for k in self.resource_group_metrics: - if k: - k.validate() + pass def to_map(self): _map = super().to_map() @@ -12779,44 +13174,32 @@ def to_map(self): return _map result = dict() - if self.request_id is not None: - result['RequestId'] = self.request_id - if self.resource_group_id is not None: - result['ResourceGroupID'] = self.resource_group_id - result['ResourceGroupMetrics'] = [] - if self.resource_group_metrics is not None: - for k in self.resource_group_metrics: - result['ResourceGroupMetrics'].append(k.to_map() if k else None) + if self.timestamp is not None: + result['Timestamp'] = self.timestamp + if self.value is not None: + result['Value'] = self.value return result def from_map(self, m: dict = None): m = m or dict() - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - if m.get('ResourceGroupID') is not None: - self.resource_group_id = m.get('ResourceGroupID') - self.resource_group_metrics = [] - if m.get('ResourceGroupMetrics') is not None: - for k in m.get('ResourceGroupMetrics'): - temp_model = ResourceGroupMetric() - self.resource_group_metrics.append(temp_model.from_map(k)) + if m.get('Timestamp') is not None: + self.timestamp = m.get('Timestamp') + if m.get('Value') is not None: + self.value = m.get('Value') return self -class GetResourceGroupMetricsResponse(TeaModel): +class GetTrainingJobResponseBodyLatestProgressRemainingTime(TeaModel): def __init__( self, - headers: Dict[str, str] = None, - status_code: int = None, - body: GetResourceGroupMetricsResponseBody = None, + timestamp: str = None, + value: int = None, ): - self.headers = headers - self.status_code = status_code - self.body = body + self.timestamp = timestamp + self.value = value def validate(self): - if self.body: - self.body.validate() + pass def to_map(self): _map = super().to_map() @@ -12824,37 +13207,35 @@ def to_map(self): return _map result = dict() - if self.headers is not None: - result['headers'] = self.headers - if self.status_code is not None: - result['statusCode'] = self.status_code - if self.body is not None: - result['body'] = self.body.to_map() + if self.timestamp is not None: + result['Timestamp'] = self.timestamp + if self.value is not None: + result['Value'] = self.value return result def from_map(self, m: dict = None): m = m or dict() - if m.get('headers') is not None: - self.headers = m.get('headers') - if m.get('statusCode') is not None: - self.status_code = m.get('statusCode') - if m.get('body') is not None: - temp_model = GetResourceGroupMetricsResponseBody() - self.body = temp_model.from_map(m['body']) - return self - - -class GetResourceGroupRequestRequest(TeaModel): + if m.get('Timestamp') is not None: + self.timestamp = m.get('Timestamp') + if m.get('Value') is not None: + self.value = m.get('Value') + return self + + +class GetTrainingJobResponseBodyLatestProgress(TeaModel): def __init__( self, - pod_status: str = None, - resource_group_id: str = None, + overall_progress: GetTrainingJobResponseBodyLatestProgressOverallProgress = None, + remaining_time: GetTrainingJobResponseBodyLatestProgressRemainingTime = None, ): - self.pod_status = pod_status - self.resource_group_id = resource_group_id + self.overall_progress = overall_progress + self.remaining_time = remaining_time def validate(self): - pass + if self.overall_progress: + self.overall_progress.validate() + if self.remaining_time: + self.remaining_time.validate() def to_map(self): _map = super().to_map() @@ -12862,39 +13243,36 @@ def to_map(self): return _map result = dict() - if self.pod_status is not None: - result['PodStatus'] = self.pod_status - if self.resource_group_id is not None: - result['ResourceGroupID'] = self.resource_group_id + if self.overall_progress is not None: + result['OverallProgress'] = self.overall_progress.to_map() + if self.remaining_time is not None: + result['RemainingTime'] = self.remaining_time.to_map() return result def from_map(self, m: dict = None): m = m or dict() - if m.get('PodStatus') is not None: - self.pod_status = m.get('PodStatus') - if m.get('ResourceGroupID') is not None: - self.resource_group_id = m.get('ResourceGroupID') + if m.get('OverallProgress') is not None: + temp_model = GetTrainingJobResponseBodyLatestProgressOverallProgress() + self.overall_progress = temp_model.from_map(m['OverallProgress']) + if m.get('RemainingTime') is not None: + temp_model = GetTrainingJobResponseBodyLatestProgressRemainingTime() + self.remaining_time = temp_model.from_map(m['RemainingTime']) return self -class GetResourceGroupRequestResponseBody(TeaModel): +class GetTrainingJobResponseBodyOutputChannels(TeaModel): def __init__( self, - request_cpu: int = None, - request_gpu: int = None, - request_gpuinfos: List[GPUInfo] = None, - request_memory: int = None, + dataset_id: str = None, + name: str = None, + output_uri: str = None, ): - self.request_cpu = request_cpu - self.request_gpu = request_gpu - self.request_gpuinfos = request_gpuinfos - self.request_memory = request_memory + self.dataset_id = dataset_id + self.name = name + self.output_uri = output_uri def validate(self): - if self.request_gpuinfos: - for k in self.request_gpuinfos: - if k: - k.validate() + pass def to_map(self): _map = super().to_map() @@ -12902,48 +13280,36 @@ def to_map(self): return _map result = dict() - if self.request_cpu is not None: - result['requestCPU'] = self.request_cpu - if self.request_gpu is not None: - result['requestGPU'] = self.request_gpu - result['requestGPUInfos'] = [] - if self.request_gpuinfos is not None: - for k in self.request_gpuinfos: - result['requestGPUInfos'].append(k.to_map() if k else None) - if self.request_memory is not None: - result['requestMemory'] = self.request_memory + if self.dataset_id is not None: + result['DatasetId'] = self.dataset_id + if self.name is not None: + result['Name'] = self.name + if self.output_uri is not None: + result['OutputUri'] = self.output_uri return result def from_map(self, m: dict = None): m = m or dict() - if m.get('requestCPU') is not None: - self.request_cpu = m.get('requestCPU') - if m.get('requestGPU') is not None: - self.request_gpu = m.get('requestGPU') - self.request_gpuinfos = [] - if m.get('requestGPUInfos') is not None: - for k in m.get('requestGPUInfos'): - temp_model = GPUInfo() - self.request_gpuinfos.append(temp_model.from_map(k)) - if m.get('requestMemory') is not None: - self.request_memory = m.get('requestMemory') + if m.get('DatasetId') is not None: + self.dataset_id = m.get('DatasetId') + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('OutputUri') is not None: + self.output_uri = m.get('OutputUri') return self -class GetResourceGroupRequestResponse(TeaModel): +class GetTrainingJobResponseBodyOutputModel(TeaModel): def __init__( self, - headers: Dict[str, str] = None, - status_code: int = None, - body: GetResourceGroupRequestResponseBody = None, + output_channel_name: str = None, + uri: str = None, ): - self.headers = headers - self.status_code = status_code - self.body = body + self.output_channel_name = output_channel_name + self.uri = uri def validate(self): - if self.body: - self.body.validate() + pass def to_map(self): _map = super().to_map() @@ -12951,32 +13317,27 @@ def to_map(self): return _map result = dict() - if self.headers is not None: - result['headers'] = self.headers - if self.status_code is not None: - result['statusCode'] = self.status_code - if self.body is not None: - result['body'] = self.body.to_map() + if self.output_channel_name is not None: + result['OutputChannelName'] = self.output_channel_name + if self.uri is not None: + result['Uri'] = self.uri return result def from_map(self, m: dict = None): m = m or dict() - if m.get('headers') is not None: - self.headers = m.get('headers') - if m.get('statusCode') is not None: - self.status_code = m.get('statusCode') - if m.get('body') is not None: - temp_model = GetResourceGroupRequestResponseBody() - self.body = temp_model.from_map(m['body']) + if m.get('OutputChannelName') is not None: + self.output_channel_name = m.get('OutputChannelName') + if m.get('Uri') is not None: + self.uri = m.get('Uri') return self -class GetResourceGroupTotalRequest(TeaModel): +class GetTrainingJobResponseBodyScheduler(TeaModel): def __init__( self, - resource_group_id: str = None, + max_running_time_in_seconds: int = None, ): - self.resource_group_id = resource_group_id + self.max_running_time_in_seconds = max_running_time_in_seconds def validate(self): pass @@ -12987,35 +13348,32 @@ def to_map(self): return _map result = dict() - if self.resource_group_id is not None: - result['ResourceGroupID'] = self.resource_group_id + if self.max_running_time_in_seconds is not None: + result['MaxRunningTimeInSeconds'] = self.max_running_time_in_seconds return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ResourceGroupID') is not None: - self.resource_group_id = m.get('ResourceGroupID') + if m.get('MaxRunningTimeInSeconds') is not None: + self.max_running_time_in_seconds = m.get('MaxRunningTimeInSeconds') return self -class GetResourceGroupTotalResponseBody(TeaModel): +class GetTrainingJobResponseBodySettings(TeaModel): def __init__( self, - total_cpu: int = None, - total_gpu: int = None, - total_gpuinfos: List[GPUInfo] = None, - total_memory: int = None, + aimaster_type: str = None, + enable_error_monitoring_in_aimaster: bool = None, + error_monitoring_args: str = None, + priority: int = None, ): - self.total_cpu = total_cpu - self.total_gpu = total_gpu - self.total_gpuinfos = total_gpuinfos - self.total_memory = total_memory + self.aimaster_type = aimaster_type + self.enable_error_monitoring_in_aimaster = enable_error_monitoring_in_aimaster + self.error_monitoring_args = error_monitoring_args + self.priority = priority def validate(self): - if self.total_gpuinfos: - for k in self.total_gpuinfos: - if k: - k.validate() + pass def to_map(self): _map = super().to_map() @@ -13023,48 +13381,46 @@ def to_map(self): return _map result = dict() - if self.total_cpu is not None: - result['totalCPU'] = self.total_cpu - if self.total_gpu is not None: - result['totalGPU'] = self.total_gpu - result['totalGPUInfos'] = [] - if self.total_gpuinfos is not None: - for k in self.total_gpuinfos: - result['totalGPUInfos'].append(k.to_map() if k else None) - if self.total_memory is not None: - result['totalMemory'] = self.total_memory + if self.aimaster_type is not None: + result['AIMasterType'] = self.aimaster_type + if self.enable_error_monitoring_in_aimaster is not None: + result['EnableErrorMonitoringInAIMaster'] = self.enable_error_monitoring_in_aimaster + if self.error_monitoring_args is not None: + result['ErrorMonitoringArgs'] = self.error_monitoring_args + if self.priority is not None: + result['Priority'] = self.priority return result def from_map(self, m: dict = None): m = m or dict() - if m.get('totalCPU') is not None: - self.total_cpu = m.get('totalCPU') - if m.get('totalGPU') is not None: - self.total_gpu = m.get('totalGPU') - self.total_gpuinfos = [] - if m.get('totalGPUInfos') is not None: - for k in m.get('totalGPUInfos'): - temp_model = GPUInfo() - self.total_gpuinfos.append(temp_model.from_map(k)) - if m.get('totalMemory') is not None: - self.total_memory = m.get('totalMemory') + if m.get('AIMasterType') is not None: + self.aimaster_type = m.get('AIMasterType') + if m.get('EnableErrorMonitoringInAIMaster') is not None: + self.enable_error_monitoring_in_aimaster = m.get('EnableErrorMonitoringInAIMaster') + if m.get('ErrorMonitoringArgs') is not None: + self.error_monitoring_args = m.get('ErrorMonitoringArgs') + if m.get('Priority') is not None: + self.priority = m.get('Priority') return self -class GetResourceGroupTotalResponse(TeaModel): +class GetTrainingJobResponseBodyStatusTransitions(TeaModel): def __init__( self, - headers: Dict[str, str] = None, - status_code: int = None, - body: GetResourceGroupTotalResponseBody = None, + end_time: str = None, + reason_code: str = None, + reason_message: str = None, + start_time: str = None, + status: str = None, ): - self.headers = headers - self.status_code = status_code - self.body = body + self.end_time = end_time + self.reason_code = reason_code + self.reason_message = reason_message + self.start_time = start_time + self.status = status def validate(self): - if self.body: - self.body.validate() + pass def to_map(self): _map = super().to_map() @@ -13072,79 +13428,48 @@ def to_map(self): return _map result = dict() - if self.headers is not None: - result['headers'] = self.headers - if self.status_code is not None: - result['statusCode'] = self.status_code - if self.body is not None: - result['body'] = self.body.to_map() + if self.end_time is not None: + result['EndTime'] = self.end_time + if self.reason_code is not None: + result['ReasonCode'] = self.reason_code + if self.reason_message is not None: + result['ReasonMessage'] = self.reason_message + if self.start_time is not None: + result['StartTime'] = self.start_time + if self.status is not None: + result['Status'] = self.status return result def from_map(self, m: dict = None): m = m or dict() - if m.get('headers') is not None: - self.headers = m.get('headers') - if m.get('statusCode') is not None: - self.status_code = m.get('statusCode') - if m.get('body') is not None: - temp_model = GetResourceGroupTotalResponseBody() - self.body = temp_model.from_map(m['body']) - return self - - -class GetServiceIdentityRoleResponseBody(TeaModel): - def __init__( - self, - exist: bool = None, - request_id: str = None, - role_name: str = None, - ): - self.exist = exist - self.request_id = request_id - self.role_name = role_name - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.exist is not None: - result['Exist'] = self.exist - if self.request_id is not None: - result['RequestId'] = self.request_id - if self.role_name is not None: - result['RoleName'] = self.role_name - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('Exist') is not None: - self.exist = m.get('Exist') - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - if m.get('RoleName') is not None: - self.role_name = m.get('RoleName') + if m.get('EndTime') is not None: + self.end_time = m.get('EndTime') + if m.get('ReasonCode') is not None: + self.reason_code = m.get('ReasonCode') + if m.get('ReasonMessage') is not None: + self.reason_message = m.get('ReasonMessage') + if m.get('StartTime') is not None: + self.start_time = m.get('StartTime') + if m.get('Status') is not None: + self.status = m.get('Status') return self -class GetServiceIdentityRoleResponse(TeaModel): +class GetTrainingJobResponseBodyUserVpc(TeaModel): def __init__( self, - headers: Dict[str, str] = None, - status_code: int = None, - body: GetServiceIdentityRoleResponseBody = None, + extended_cidrs: List[str] = None, + security_group_id: str = None, + switch_id: str = None, + vpc_id: str = None, ): - self.headers = headers - self.status_code = status_code - self.body = body + self.extended_cidrs = extended_cidrs + self.security_group_id = security_group_id + self.switch_id = switch_id + self.vpc_id = vpc_id def validate(self): - if self.body: - self.body.validate() + pass def to_map(self): _map = super().to_map() @@ -13152,45 +13477,151 @@ def to_map(self): return _map result = dict() - if self.headers is not None: - result['headers'] = self.headers - if self.status_code is not None: - result['statusCode'] = self.status_code - if self.body is not None: - result['body'] = self.body.to_map() + if self.extended_cidrs is not None: + result['ExtendedCIDRs'] = self.extended_cidrs + if self.security_group_id is not None: + result['SecurityGroupId'] = self.security_group_id + if self.switch_id is not None: + result['SwitchId'] = self.switch_id + if self.vpc_id is not None: + result['VpcId'] = self.vpc_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('headers') is not None: - self.headers = m.get('headers') - if m.get('statusCode') is not None: - self.status_code = m.get('statusCode') - if m.get('body') is not None: - temp_model = GetServiceIdentityRoleResponseBody() - self.body = temp_model.from_map(m['body']) + if m.get('ExtendedCIDRs') is not None: + self.extended_cidrs = m.get('ExtendedCIDRs') + if m.get('SecurityGroupId') is not None: + self.security_group_id = m.get('SecurityGroupId') + if m.get('SwitchId') is not None: + self.switch_id = m.get('SwitchId') + if m.get('VpcId') is not None: + self.vpc_id = m.get('VpcId') return self -class GetSpotPriceHistoryRequest(TeaModel): +class GetTrainingJobResponseBody(TeaModel): def __init__( self, - end_time: str = None, - order: str = None, - page_number: int = None, - page_size: int = None, - sort_by: str = None, - start_time: str = None, + algorithm_id: str = None, + algorithm_name: str = None, + algorithm_provider: str = None, + algorithm_spec: AlgorithmSpec = None, + algorithm_version: str = None, + compute_resource: GetTrainingJobResponseBodyComputeResource = None, + duration: int = None, + environments: Dict[str, str] = None, + experiment_config: GetTrainingJobResponseBodyExperimentConfig = None, + gmt_create_time: str = None, + gmt_modified_time: str = None, + hyper_parameters: List[GetTrainingJobResponseBodyHyperParameters] = None, + input_channels: List[GetTrainingJobResponseBodyInputChannels] = None, + instances: List[GetTrainingJobResponseBodyInstances] = None, + is_temp_algo: bool = None, + labels: List[GetTrainingJobResponseBodyLabels] = None, + latest_metrics: List[GetTrainingJobResponseBodyLatestMetrics] = None, + latest_progress: GetTrainingJobResponseBodyLatestProgress = None, + output_channels: List[GetTrainingJobResponseBodyOutputChannels] = None, + output_model: GetTrainingJobResponseBodyOutputModel = None, + python_requirements: List[str] = None, + reason_code: str = None, + reason_message: str = None, + request_id: str = None, + role_arn: str = None, + scheduler: GetTrainingJobResponseBodyScheduler = None, + settings: GetTrainingJobResponseBodySettings = None, + status: str = None, + status_transitions: List[GetTrainingJobResponseBodyStatusTransitions] = None, + training_job_description: str = None, + training_job_id: str = None, + training_job_name: str = None, + training_job_url: str = None, + user_id: str = None, + user_vpc: GetTrainingJobResponseBodyUserVpc = None, + workspace_id: str = None, ): - self.end_time = end_time - self.order = order - self.page_number = page_number - self.page_size = page_size - self.sort_by = sort_by - self.start_time = start_time + self.algorithm_id = algorithm_id + self.algorithm_name = algorithm_name + self.algorithm_provider = algorithm_provider + self.algorithm_spec = algorithm_spec + self.algorithm_version = algorithm_version + self.compute_resource = compute_resource + self.duration = duration + self.environments = environments + self.experiment_config = experiment_config + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time + self.hyper_parameters = hyper_parameters + self.input_channels = input_channels + self.instances = instances + self.is_temp_algo = is_temp_algo + self.labels = labels + self.latest_metrics = latest_metrics + self.latest_progress = latest_progress + self.output_channels = output_channels + self.output_model = output_model + self.python_requirements = python_requirements + self.reason_code = reason_code + self.reason_message = reason_message + self.request_id = request_id + self.role_arn = role_arn + self.scheduler = scheduler + self.settings = settings + self.status = status + self.status_transitions = status_transitions + self.training_job_description = training_job_description + self.training_job_id = training_job_id + self.training_job_name = training_job_name + self.training_job_url = training_job_url + self.user_id = user_id + self.user_vpc = user_vpc + self.workspace_id = workspace_id def validate(self): - pass + if self.algorithm_spec: + self.algorithm_spec.validate() + if self.compute_resource: + self.compute_resource.validate() + if self.experiment_config: + self.experiment_config.validate() + if self.hyper_parameters: + for k in self.hyper_parameters: + if k: + k.validate() + if self.input_channels: + for k in self.input_channels: + if k: + k.validate() + if self.instances: + for k in self.instances: + if k: + k.validate() + if self.labels: + for k in self.labels: + if k: + k.validate() + if self.latest_metrics: + for k in self.latest_metrics: + if k: + k.validate() + if self.latest_progress: + self.latest_progress.validate() + if self.output_channels: + for k in self.output_channels: + if k: + k.validate() + if self.output_model: + self.output_model.validate() + if self.scheduler: + self.scheduler.validate() + if self.settings: + self.settings.validate() + if self.status_transitions: + for k in self.status_transitions: + if k: + k.validate() + if self.user_vpc: + self.user_vpc.validate() def to_map(self): _map = super().to_map() @@ -13198,1788 +13629,206 @@ def to_map(self): return _map result = dict() - if self.end_time is not None: - result['EndTime'] = self.end_time - if self.order is not None: - result['Order'] = self.order - if self.page_number is not None: - result['PageNumber'] = self.page_number - if self.page_size is not None: - result['PageSize'] = self.page_size - if self.sort_by is not None: - result['SortBy'] = self.sort_by - if self.start_time is not None: - result['StartTime'] = self.start_time - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('EndTime') is not None: - self.end_time = m.get('EndTime') - if m.get('Order') is not None: - self.order = m.get('Order') - if m.get('PageNumber') is not None: - self.page_number = m.get('PageNumber') - if m.get('PageSize') is not None: - self.page_size = m.get('PageSize') - if m.get('SortBy') is not None: - self.sort_by = m.get('SortBy') - if m.get('StartTime') is not None: - self.start_time = m.get('StartTime') - return self - - -class GetSpotPriceHistoryResponseBody(TeaModel): - def __init__( - self, - request_id: str = None, - spot_price_history: List[SpotPriceItem] = None, - total_count: int = None, - ): - self.request_id = request_id - self.spot_price_history = spot_price_history - self.total_count = total_count - - def validate(self): - if self.spot_price_history: - for k in self.spot_price_history: - if k: - k.validate() - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.request_id is not None: - result['RequestId'] = self.request_id - result['SpotPriceHistory'] = [] - if self.spot_price_history is not None: - for k in self.spot_price_history: - result['SpotPriceHistory'].append(k.to_map() if k else None) - if self.total_count is not None: - result['TotalCount'] = self.total_count - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - self.spot_price_history = [] - if m.get('SpotPriceHistory') is not None: - for k in m.get('SpotPriceHistory'): - temp_model = SpotPriceItem() - self.spot_price_history.append(temp_model.from_map(k)) - if m.get('TotalCount') is not None: - self.total_count = m.get('TotalCount') - return self - - -class GetSpotPriceHistoryResponse(TeaModel): - def __init__( - self, - headers: Dict[str, str] = None, - status_code: int = None, - body: GetSpotPriceHistoryResponseBody = None, - ): - self.headers = headers - self.status_code = status_code - self.body = body - - def validate(self): - if self.body: - self.body.validate() - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.headers is not None: - result['headers'] = self.headers - if self.status_code is not None: - result['statusCode'] = self.status_code - if self.body is not None: - result['body'] = self.body.to_map() - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('headers') is not None: - self.headers = m.get('headers') - if m.get('statusCode') is not None: - self.status_code = m.get('statusCode') - if m.get('body') is not None: - temp_model = GetSpotPriceHistoryResponseBody() - self.body = temp_model.from_map(m['body']) - return self - - -class GetSpotStockPreviewResponseBody(TeaModel): - def __init__( - self, - instance_type: str = None, - request_id: str = None, - stock_status: str = None, - ): - self.instance_type = instance_type - self.request_id = request_id - self.stock_status = stock_status - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.instance_type is not None: - result['InstanceType'] = self.instance_type - if self.request_id is not None: - result['RequestId'] = self.request_id - if self.stock_status is not None: - result['StockStatus'] = self.stock_status - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('InstanceType') is not None: - self.instance_type = m.get('InstanceType') - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - if m.get('StockStatus') is not None: - self.stock_status = m.get('StockStatus') - return self - - -class GetSpotStockPreviewResponse(TeaModel): - def __init__( - self, - headers: Dict[str, str] = None, - status_code: int = None, - body: GetSpotStockPreviewResponseBody = None, - ): - self.headers = headers - self.status_code = status_code - self.body = body - - def validate(self): - if self.body: - self.body.validate() - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.headers is not None: - result['headers'] = self.headers - if self.status_code is not None: - result['statusCode'] = self.status_code - if self.body is not None: - result['body'] = self.body.to_map() - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('headers') is not None: - self.headers = m.get('headers') - if m.get('statusCode') is not None: - self.status_code = m.get('statusCode') - if m.get('body') is not None: - temp_model = GetSpotStockPreviewResponseBody() - self.body = temp_model.from_map(m['body']) - return self - - -class GetTokenRequest(TeaModel): - def __init__( - self, - expire_time: int = None, - training_job_id: str = None, - ): - self.expire_time = expire_time - self.training_job_id = training_job_id - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.expire_time is not None: - result['ExpireTime'] = self.expire_time - if self.training_job_id is not None: - result['TrainingJobId'] = self.training_job_id - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('ExpireTime') is not None: - self.expire_time = m.get('ExpireTime') - if m.get('TrainingJobId') is not None: - self.training_job_id = m.get('TrainingJobId') - return self - - -class GetTokenResponseBody(TeaModel): - def __init__( - self, - request_id: str = None, - token: str = None, - ): - self.request_id = request_id - self.token = token - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.request_id is not None: - result['RequestId'] = self.request_id - if self.token is not None: - result['Token'] = self.token - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - if m.get('Token') is not None: - self.token = m.get('Token') - return self - - -class GetTokenResponse(TeaModel): - def __init__( - self, - headers: Dict[str, str] = None, - status_code: int = None, - body: GetTokenResponseBody = None, - ): - self.headers = headers - self.status_code = status_code - self.body = body - - def validate(self): - if self.body: - self.body.validate() - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.headers is not None: - result['headers'] = self.headers - if self.status_code is not None: - result['statusCode'] = self.status_code - if self.body is not None: - result['body'] = self.body.to_map() - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('headers') is not None: - self.headers = m.get('headers') - if m.get('statusCode') is not None: - self.status_code = m.get('statusCode') - if m.get('body') is not None: - temp_model = GetTokenResponseBody() - self.body = temp_model.from_map(m['body']) - return self - - -class GetTrainingJobRequest(TeaModel): - def __init__( - self, - token: str = None, - ): - self.token = token - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.token is not None: - result['Token'] = self.token - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('Token') is not None: - self.token = m.get('Token') - return self - - -class GetTrainingJobResponseBodyComputeResourceInstanceSpec(TeaModel): - def __init__( - self, - cpu: str = None, - gpu: str = None, - gputype: str = None, - memory: str = None, - shared_memory: str = None, - ): - self.cpu = cpu - self.gpu = gpu - self.gputype = gputype - self.memory = memory - self.shared_memory = shared_memory - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.cpu is not None: - result['CPU'] = self.cpu - if self.gpu is not None: - result['GPU'] = self.gpu - if self.gputype is not None: - result['GPUType'] = self.gputype - if self.memory is not None: - result['Memory'] = self.memory - if self.shared_memory is not None: - result['SharedMemory'] = self.shared_memory - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('CPU') is not None: - self.cpu = m.get('CPU') - if m.get('GPU') is not None: - self.gpu = m.get('GPU') - if m.get('GPUType') is not None: - self.gputype = m.get('GPUType') - if m.get('Memory') is not None: - self.memory = m.get('Memory') - if m.get('SharedMemory') is not None: - self.shared_memory = m.get('SharedMemory') - return self - - -class GetTrainingJobResponseBodyComputeResource(TeaModel): - def __init__( - self, - ecs_count: int = None, - ecs_spec: str = None, - instance_count: int = None, - instance_spec: GetTrainingJobResponseBodyComputeResourceInstanceSpec = None, - resource_id: str = None, - ): - self.ecs_count = ecs_count - self.ecs_spec = ecs_spec - self.instance_count = instance_count - self.instance_spec = instance_spec - self.resource_id = resource_id - - def validate(self): - if self.instance_spec: - self.instance_spec.validate() - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.ecs_count is not None: - result['EcsCount'] = self.ecs_count - if self.ecs_spec is not None: - result['EcsSpec'] = self.ecs_spec - if self.instance_count is not None: - result['InstanceCount'] = self.instance_count - if self.instance_spec is not None: - result['InstanceSpec'] = self.instance_spec.to_map() - if self.resource_id is not None: - result['ResourceId'] = self.resource_id - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('EcsCount') is not None: - self.ecs_count = m.get('EcsCount') - if m.get('EcsSpec') is not None: - self.ecs_spec = m.get('EcsSpec') - if m.get('InstanceCount') is not None: - self.instance_count = m.get('InstanceCount') - if m.get('InstanceSpec') is not None: - temp_model = GetTrainingJobResponseBodyComputeResourceInstanceSpec() - self.instance_spec = temp_model.from_map(m['InstanceSpec']) - if m.get('ResourceId') is not None: - self.resource_id = m.get('ResourceId') - return self - - -class GetTrainingJobResponseBodyExperimentConfig(TeaModel): - def __init__( - self, - experiment_id: str = None, - experiment_name: str = None, - ): - self.experiment_id = experiment_id - self.experiment_name = experiment_name - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.experiment_id is not None: - result['ExperimentId'] = self.experiment_id - if self.experiment_name is not None: - result['ExperimentName'] = self.experiment_name - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('ExperimentId') is not None: - self.experiment_id = m.get('ExperimentId') - if m.get('ExperimentName') is not None: - self.experiment_name = m.get('ExperimentName') - return self - - -class GetTrainingJobResponseBodyHyperParameters(TeaModel): - def __init__( - self, - name: str = None, - value: str = None, - ): - self.name = name - self.value = value - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.name is not None: - result['Name'] = self.name - if self.value is not None: - result['Value'] = self.value - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('Name') is not None: - self.name = m.get('Name') - if m.get('Value') is not None: - self.value = m.get('Value') - return self - - -class GetTrainingJobResponseBodyInputChannels(TeaModel): - def __init__( - self, - dataset_id: str = None, - input_uri: str = None, - name: str = None, - ): - self.dataset_id = dataset_id - self.input_uri = input_uri - self.name = name - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.dataset_id is not None: - result['DatasetId'] = self.dataset_id - if self.input_uri is not None: - result['InputUri'] = self.input_uri - if self.name is not None: - result['Name'] = self.name - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('DatasetId') is not None: - self.dataset_id = m.get('DatasetId') - if m.get('InputUri') is not None: - self.input_uri = m.get('InputUri') - if m.get('Name') is not None: - self.name = m.get('Name') - return self - - -class GetTrainingJobResponseBodyInstances(TeaModel): - def __init__( - self, - name: str = None, - role: str = None, - status: str = None, - ): - self.name = name - self.role = role - self.status = status - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.name is not None: - result['Name'] = self.name - if self.role is not None: - result['Role'] = self.role - if self.status is not None: - result['Status'] = self.status - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('Name') is not None: - self.name = m.get('Name') - if m.get('Role') is not None: - self.role = m.get('Role') - if m.get('Status') is not None: - self.status = m.get('Status') - return self - - -class GetTrainingJobResponseBodyLabels(TeaModel): - def __init__( - self, - key: str = None, - value: str = None, - ): - self.key = key - self.value = value - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.key is not None: - result['Key'] = self.key - if self.value is not None: - result['Value'] = self.value - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('Key') is not None: - self.key = m.get('Key') - if m.get('Value') is not None: - self.value = m.get('Value') - return self - - -class GetTrainingJobResponseBodyLatestMetrics(TeaModel): - def __init__( - self, - name: str = None, - timestamp: str = None, - value: float = None, - ): - self.name = name - self.timestamp = timestamp - self.value = value - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.name is not None: - result['Name'] = self.name - if self.timestamp is not None: - result['Timestamp'] = self.timestamp - if self.value is not None: - result['Value'] = self.value - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('Name') is not None: - self.name = m.get('Name') - if m.get('Timestamp') is not None: - self.timestamp = m.get('Timestamp') - if m.get('Value') is not None: - self.value = m.get('Value') - return self - - -class GetTrainingJobResponseBodyLatestProgressOverallProgress(TeaModel): - def __init__( - self, - timestamp: str = None, - value: float = None, - ): - self.timestamp = timestamp - self.value = value - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.timestamp is not None: - result['Timestamp'] = self.timestamp - if self.value is not None: - result['Value'] = self.value - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('Timestamp') is not None: - self.timestamp = m.get('Timestamp') - if m.get('Value') is not None: - self.value = m.get('Value') - return self - - -class GetTrainingJobResponseBodyLatestProgressRemainingTime(TeaModel): - def __init__( - self, - timestamp: str = None, - value: int = None, - ): - self.timestamp = timestamp - self.value = value - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.timestamp is not None: - result['Timestamp'] = self.timestamp - if self.value is not None: - result['Value'] = self.value - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('Timestamp') is not None: - self.timestamp = m.get('Timestamp') - if m.get('Value') is not None: - self.value = m.get('Value') - return self - - -class GetTrainingJobResponseBodyLatestProgress(TeaModel): - def __init__( - self, - overall_progress: GetTrainingJobResponseBodyLatestProgressOverallProgress = None, - remaining_time: GetTrainingJobResponseBodyLatestProgressRemainingTime = None, - ): - self.overall_progress = overall_progress - self.remaining_time = remaining_time - - def validate(self): - if self.overall_progress: - self.overall_progress.validate() - if self.remaining_time: - self.remaining_time.validate() - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.overall_progress is not None: - result['OverallProgress'] = self.overall_progress.to_map() - if self.remaining_time is not None: - result['RemainingTime'] = self.remaining_time.to_map() - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('OverallProgress') is not None: - temp_model = GetTrainingJobResponseBodyLatestProgressOverallProgress() - self.overall_progress = temp_model.from_map(m['OverallProgress']) - if m.get('RemainingTime') is not None: - temp_model = GetTrainingJobResponseBodyLatestProgressRemainingTime() - self.remaining_time = temp_model.from_map(m['RemainingTime']) - return self - - -class GetTrainingJobResponseBodyOutputChannels(TeaModel): - def __init__( - self, - dataset_id: str = None, - name: str = None, - output_uri: str = None, - ): - self.dataset_id = dataset_id - self.name = name - self.output_uri = output_uri - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.dataset_id is not None: - result['DatasetId'] = self.dataset_id - if self.name is not None: - result['Name'] = self.name - if self.output_uri is not None: - result['OutputUri'] = self.output_uri - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('DatasetId') is not None: - self.dataset_id = m.get('DatasetId') - if m.get('Name') is not None: - self.name = m.get('Name') - if m.get('OutputUri') is not None: - self.output_uri = m.get('OutputUri') - return self - - -class GetTrainingJobResponseBodyOutputModel(TeaModel): - def __init__( - self, - output_channel_name: str = None, - uri: str = None, - ): - self.output_channel_name = output_channel_name - self.uri = uri - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.output_channel_name is not None: - result['OutputChannelName'] = self.output_channel_name - if self.uri is not None: - result['Uri'] = self.uri - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('OutputChannelName') is not None: - self.output_channel_name = m.get('OutputChannelName') - if m.get('Uri') is not None: - self.uri = m.get('Uri') - return self - - -class GetTrainingJobResponseBodyScheduler(TeaModel): - def __init__( - self, - max_running_time_in_seconds: int = None, - ): - self.max_running_time_in_seconds = max_running_time_in_seconds - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.max_running_time_in_seconds is not None: - result['MaxRunningTimeInSeconds'] = self.max_running_time_in_seconds - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('MaxRunningTimeInSeconds') is not None: - self.max_running_time_in_seconds = m.get('MaxRunningTimeInSeconds') - return self - - -class GetTrainingJobResponseBodySettings(TeaModel): - def __init__( - self, - aimaster_type: str = None, - enable_error_monitoring_in_aimaster: bool = None, - error_monitoring_args: str = None, - priority: int = None, - ): - self.aimaster_type = aimaster_type - self.enable_error_monitoring_in_aimaster = enable_error_monitoring_in_aimaster - self.error_monitoring_args = error_monitoring_args - self.priority = priority - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.aimaster_type is not None: - result['AIMasterType'] = self.aimaster_type - if self.enable_error_monitoring_in_aimaster is not None: - result['EnableErrorMonitoringInAIMaster'] = self.enable_error_monitoring_in_aimaster - if self.error_monitoring_args is not None: - result['ErrorMonitoringArgs'] = self.error_monitoring_args - if self.priority is not None: - result['Priority'] = self.priority - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('AIMasterType') is not None: - self.aimaster_type = m.get('AIMasterType') - if m.get('EnableErrorMonitoringInAIMaster') is not None: - self.enable_error_monitoring_in_aimaster = m.get('EnableErrorMonitoringInAIMaster') - if m.get('ErrorMonitoringArgs') is not None: - self.error_monitoring_args = m.get('ErrorMonitoringArgs') - if m.get('Priority') is not None: - self.priority = m.get('Priority') - return self - - -class GetTrainingJobResponseBodyStatusTransitions(TeaModel): - def __init__( - self, - end_time: str = None, - reason_code: str = None, - reason_message: str = None, - start_time: str = None, - status: str = None, - ): - self.end_time = end_time - self.reason_code = reason_code - self.reason_message = reason_message - self.start_time = start_time - self.status = status - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.end_time is not None: - result['EndTime'] = self.end_time - if self.reason_code is not None: - result['ReasonCode'] = self.reason_code - if self.reason_message is not None: - result['ReasonMessage'] = self.reason_message - if self.start_time is not None: - result['StartTime'] = self.start_time - if self.status is not None: - result['Status'] = self.status - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('EndTime') is not None: - self.end_time = m.get('EndTime') - if m.get('ReasonCode') is not None: - self.reason_code = m.get('ReasonCode') - if m.get('ReasonMessage') is not None: - self.reason_message = m.get('ReasonMessage') - if m.get('StartTime') is not None: - self.start_time = m.get('StartTime') - if m.get('Status') is not None: - self.status = m.get('Status') - return self - - -class GetTrainingJobResponseBodyUserVpc(TeaModel): - def __init__( - self, - extended_cidrs: List[str] = None, - security_group_id: str = None, - switch_id: str = None, - vpc_id: str = None, - ): - self.extended_cidrs = extended_cidrs - self.security_group_id = security_group_id - self.switch_id = switch_id - self.vpc_id = vpc_id - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.extended_cidrs is not None: - result['ExtendedCIDRs'] = self.extended_cidrs - if self.security_group_id is not None: - result['SecurityGroupId'] = self.security_group_id - if self.switch_id is not None: - result['SwitchId'] = self.switch_id - if self.vpc_id is not None: - result['VpcId'] = self.vpc_id - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('ExtendedCIDRs') is not None: - self.extended_cidrs = m.get('ExtendedCIDRs') - if m.get('SecurityGroupId') is not None: - self.security_group_id = m.get('SecurityGroupId') - if m.get('SwitchId') is not None: - self.switch_id = m.get('SwitchId') - if m.get('VpcId') is not None: - self.vpc_id = m.get('VpcId') - return self - - -class GetTrainingJobResponseBody(TeaModel): - def __init__( - self, - algorithm_id: str = None, - algorithm_name: str = None, - algorithm_provider: str = None, - algorithm_spec: AlgorithmSpec = None, - algorithm_version: str = None, - compute_resource: GetTrainingJobResponseBodyComputeResource = None, - duration: int = None, - environments: Dict[str, str] = None, - experiment_config: GetTrainingJobResponseBodyExperimentConfig = None, - gmt_create_time: str = None, - gmt_modified_time: str = None, - hyper_parameters: List[GetTrainingJobResponseBodyHyperParameters] = None, - input_channels: List[GetTrainingJobResponseBodyInputChannels] = None, - instances: List[GetTrainingJobResponseBodyInstances] = None, - is_temp_algo: bool = None, - labels: List[GetTrainingJobResponseBodyLabels] = None, - latest_metrics: List[GetTrainingJobResponseBodyLatestMetrics] = None, - latest_progress: GetTrainingJobResponseBodyLatestProgress = None, - output_channels: List[GetTrainingJobResponseBodyOutputChannels] = None, - output_model: GetTrainingJobResponseBodyOutputModel = None, - python_requirements: List[str] = None, - reason_code: str = None, - reason_message: str = None, - request_id: str = None, - role_arn: str = None, - scheduler: GetTrainingJobResponseBodyScheduler = None, - settings: GetTrainingJobResponseBodySettings = None, - status: str = None, - status_transitions: List[GetTrainingJobResponseBodyStatusTransitions] = None, - training_job_description: str = None, - training_job_id: str = None, - training_job_name: str = None, - training_job_url: str = None, - user_id: str = None, - user_vpc: GetTrainingJobResponseBodyUserVpc = None, - workspace_id: str = None, - ): - self.algorithm_id = algorithm_id - self.algorithm_name = algorithm_name - self.algorithm_provider = algorithm_provider - self.algorithm_spec = algorithm_spec - self.algorithm_version = algorithm_version - self.compute_resource = compute_resource - self.duration = duration - self.environments = environments - self.experiment_config = experiment_config - self.gmt_create_time = gmt_create_time - self.gmt_modified_time = gmt_modified_time - self.hyper_parameters = hyper_parameters - self.input_channels = input_channels - self.instances = instances - self.is_temp_algo = is_temp_algo - self.labels = labels - self.latest_metrics = latest_metrics - self.latest_progress = latest_progress - self.output_channels = output_channels - self.output_model = output_model - self.python_requirements = python_requirements - self.reason_code = reason_code - self.reason_message = reason_message - self.request_id = request_id - self.role_arn = role_arn - self.scheduler = scheduler - self.settings = settings - self.status = status - self.status_transitions = status_transitions - self.training_job_description = training_job_description - self.training_job_id = training_job_id - self.training_job_name = training_job_name - self.training_job_url = training_job_url - self.user_id = user_id - self.user_vpc = user_vpc - self.workspace_id = workspace_id - - def validate(self): - if self.algorithm_spec: - self.algorithm_spec.validate() - if self.compute_resource: - self.compute_resource.validate() - if self.experiment_config: - self.experiment_config.validate() - if self.hyper_parameters: - for k in self.hyper_parameters: - if k: - k.validate() - if self.input_channels: - for k in self.input_channels: - if k: - k.validate() - if self.instances: - for k in self.instances: - if k: - k.validate() - if self.labels: - for k in self.labels: - if k: - k.validate() - if self.latest_metrics: - for k in self.latest_metrics: - if k: - k.validate() - if self.latest_progress: - self.latest_progress.validate() - if self.output_channels: - for k in self.output_channels: - if k: - k.validate() - if self.output_model: - self.output_model.validate() - if self.scheduler: - self.scheduler.validate() - if self.settings: - self.settings.validate() - if self.status_transitions: - for k in self.status_transitions: - if k: - k.validate() - if self.user_vpc: - self.user_vpc.validate() - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.algorithm_id is not None: - result['AlgorithmId'] = self.algorithm_id - if self.algorithm_name is not None: - result['AlgorithmName'] = self.algorithm_name - if self.algorithm_provider is not None: - result['AlgorithmProvider'] = self.algorithm_provider - if self.algorithm_spec is not None: - result['AlgorithmSpec'] = self.algorithm_spec.to_map() - if self.algorithm_version is not None: - result['AlgorithmVersion'] = self.algorithm_version - if self.compute_resource is not None: - result['ComputeResource'] = self.compute_resource.to_map() - if self.duration is not None: - result['Duration'] = self.duration - if self.environments is not None: - result['Environments'] = self.environments - if self.experiment_config is not None: - result['ExperimentConfig'] = self.experiment_config.to_map() - if self.gmt_create_time is not None: - result['GmtCreateTime'] = self.gmt_create_time - if self.gmt_modified_time is not None: - result['GmtModifiedTime'] = self.gmt_modified_time - result['HyperParameters'] = [] - if self.hyper_parameters is not None: - for k in self.hyper_parameters: - result['HyperParameters'].append(k.to_map() if k else None) - result['InputChannels'] = [] - if self.input_channels is not None: - for k in self.input_channels: - result['InputChannels'].append(k.to_map() if k else None) - result['Instances'] = [] - if self.instances is not None: - for k in self.instances: - result['Instances'].append(k.to_map() if k else None) - if self.is_temp_algo is not None: - result['IsTempAlgo'] = self.is_temp_algo - result['Labels'] = [] - if self.labels is not None: - for k in self.labels: - result['Labels'].append(k.to_map() if k else None) - result['LatestMetrics'] = [] - if self.latest_metrics is not None: - for k in self.latest_metrics: - result['LatestMetrics'].append(k.to_map() if k else None) - if self.latest_progress is not None: - result['LatestProgress'] = self.latest_progress.to_map() - result['OutputChannels'] = [] - if self.output_channels is not None: - for k in self.output_channels: - result['OutputChannels'].append(k.to_map() if k else None) - if self.output_model is not None: - result['OutputModel'] = self.output_model.to_map() - if self.python_requirements is not None: - result['PythonRequirements'] = self.python_requirements - if self.reason_code is not None: - result['ReasonCode'] = self.reason_code - if self.reason_message is not None: - result['ReasonMessage'] = self.reason_message - if self.request_id is not None: - result['RequestId'] = self.request_id - if self.role_arn is not None: - result['RoleArn'] = self.role_arn - if self.scheduler is not None: - result['Scheduler'] = self.scheduler.to_map() - if self.settings is not None: - result['Settings'] = self.settings.to_map() - if self.status is not None: - result['Status'] = self.status - result['StatusTransitions'] = [] - if self.status_transitions is not None: - for k in self.status_transitions: - result['StatusTransitions'].append(k.to_map() if k else None) - if self.training_job_description is not None: - result['TrainingJobDescription'] = self.training_job_description - if self.training_job_id is not None: - result['TrainingJobId'] = self.training_job_id - if self.training_job_name is not None: - result['TrainingJobName'] = self.training_job_name - if self.training_job_url is not None: - result['TrainingJobUrl'] = self.training_job_url - if self.user_id is not None: - result['UserId'] = self.user_id - if self.user_vpc is not None: - result['UserVpc'] = self.user_vpc.to_map() - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('AlgorithmId') is not None: - self.algorithm_id = m.get('AlgorithmId') - if m.get('AlgorithmName') is not None: - self.algorithm_name = m.get('AlgorithmName') - if m.get('AlgorithmProvider') is not None: - self.algorithm_provider = m.get('AlgorithmProvider') - if m.get('AlgorithmSpec') is not None: - temp_model = AlgorithmSpec() - self.algorithm_spec = temp_model.from_map(m['AlgorithmSpec']) - if m.get('AlgorithmVersion') is not None: - self.algorithm_version = m.get('AlgorithmVersion') - if m.get('ComputeResource') is not None: - temp_model = GetTrainingJobResponseBodyComputeResource() - self.compute_resource = temp_model.from_map(m['ComputeResource']) - if m.get('Duration') is not None: - self.duration = m.get('Duration') - if m.get('Environments') is not None: - self.environments = m.get('Environments') - if m.get('ExperimentConfig') is not None: - temp_model = GetTrainingJobResponseBodyExperimentConfig() - self.experiment_config = temp_model.from_map(m['ExperimentConfig']) - if m.get('GmtCreateTime') is not None: - self.gmt_create_time = m.get('GmtCreateTime') - if m.get('GmtModifiedTime') is not None: - self.gmt_modified_time = m.get('GmtModifiedTime') - self.hyper_parameters = [] - if m.get('HyperParameters') is not None: - for k in m.get('HyperParameters'): - temp_model = GetTrainingJobResponseBodyHyperParameters() - self.hyper_parameters.append(temp_model.from_map(k)) - self.input_channels = [] - if m.get('InputChannels') is not None: - for k in m.get('InputChannels'): - temp_model = GetTrainingJobResponseBodyInputChannels() - self.input_channels.append(temp_model.from_map(k)) - self.instances = [] - if m.get('Instances') is not None: - for k in m.get('Instances'): - temp_model = GetTrainingJobResponseBodyInstances() - self.instances.append(temp_model.from_map(k)) - if m.get('IsTempAlgo') is not None: - self.is_temp_algo = m.get('IsTempAlgo') - self.labels = [] - if m.get('Labels') is not None: - for k in m.get('Labels'): - temp_model = GetTrainingJobResponseBodyLabels() - self.labels.append(temp_model.from_map(k)) - self.latest_metrics = [] - if m.get('LatestMetrics') is not None: - for k in m.get('LatestMetrics'): - temp_model = GetTrainingJobResponseBodyLatestMetrics() - self.latest_metrics.append(temp_model.from_map(k)) - if m.get('LatestProgress') is not None: - temp_model = GetTrainingJobResponseBodyLatestProgress() - self.latest_progress = temp_model.from_map(m['LatestProgress']) - self.output_channels = [] - if m.get('OutputChannels') is not None: - for k in m.get('OutputChannels'): - temp_model = GetTrainingJobResponseBodyOutputChannels() - self.output_channels.append(temp_model.from_map(k)) - if m.get('OutputModel') is not None: - temp_model = GetTrainingJobResponseBodyOutputModel() - self.output_model = temp_model.from_map(m['OutputModel']) - if m.get('PythonRequirements') is not None: - self.python_requirements = m.get('PythonRequirements') - if m.get('ReasonCode') is not None: - self.reason_code = m.get('ReasonCode') - if m.get('ReasonMessage') is not None: - self.reason_message = m.get('ReasonMessage') - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - if m.get('RoleArn') is not None: - self.role_arn = m.get('RoleArn') - if m.get('Scheduler') is not None: - temp_model = GetTrainingJobResponseBodyScheduler() - self.scheduler = temp_model.from_map(m['Scheduler']) - if m.get('Settings') is not None: - temp_model = GetTrainingJobResponseBodySettings() - self.settings = temp_model.from_map(m['Settings']) - if m.get('Status') is not None: - self.status = m.get('Status') - self.status_transitions = [] - if m.get('StatusTransitions') is not None: - for k in m.get('StatusTransitions'): - temp_model = GetTrainingJobResponseBodyStatusTransitions() - self.status_transitions.append(temp_model.from_map(k)) - if m.get('TrainingJobDescription') is not None: - self.training_job_description = m.get('TrainingJobDescription') - if m.get('TrainingJobId') is not None: - self.training_job_id = m.get('TrainingJobId') - if m.get('TrainingJobName') is not None: - self.training_job_name = m.get('TrainingJobName') - if m.get('TrainingJobUrl') is not None: - self.training_job_url = m.get('TrainingJobUrl') - if m.get('UserId') is not None: - self.user_id = m.get('UserId') - if m.get('UserVpc') is not None: - temp_model = GetTrainingJobResponseBodyUserVpc() - self.user_vpc = temp_model.from_map(m['UserVpc']) - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') - return self - - -class GetTrainingJobResponse(TeaModel): - def __init__( - self, - headers: Dict[str, str] = None, - status_code: int = None, - body: GetTrainingJobResponseBody = None, - ): - self.headers = headers - self.status_code = status_code - self.body = body - - def validate(self): - if self.body: - self.body.validate() - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.headers is not None: - result['headers'] = self.headers - if self.status_code is not None: - result['statusCode'] = self.status_code - if self.body is not None: - result['body'] = self.body.to_map() - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('headers') is not None: - self.headers = m.get('headers') - if m.get('statusCode') is not None: - self.status_code = m.get('statusCode') - if m.get('body') is not None: - temp_model = GetTrainingJobResponseBody() - self.body = temp_model.from_map(m['body']) - return self - - -class GetTrainingJobErrorInfoRequest(TeaModel): - def __init__( - self, - token: str = None, - ): - self.token = token - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.token is not None: - result['Token'] = self.token - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('Token') is not None: - self.token = m.get('Token') - return self - - -class GetTrainingJobErrorInfoResponseBodyErrorInfo(TeaModel): - def __init__( - self, - additional_info: str = None, - code: str = None, - message: str = None, - ): - self.additional_info = additional_info - self.code = code - self.message = message - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.additional_info is not None: - result['AdditionalInfo'] = self.additional_info - if self.code is not None: - result['Code'] = self.code - if self.message is not None: - result['Message'] = self.message - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('AdditionalInfo') is not None: - self.additional_info = m.get('AdditionalInfo') - if m.get('Code') is not None: - self.code = m.get('Code') - if m.get('Message') is not None: - self.message = m.get('Message') - return self - - -class GetTrainingJobErrorInfoResponseBody(TeaModel): - def __init__( - self, - error_info: GetTrainingJobErrorInfoResponseBodyErrorInfo = None, - request_id: str = None, - ): - self.error_info = error_info - self.request_id = request_id - - def validate(self): - if self.error_info: - self.error_info.validate() - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.error_info is not None: - result['ErrorInfo'] = self.error_info.to_map() - if self.request_id is not None: - result['RequestId'] = self.request_id - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('ErrorInfo') is not None: - temp_model = GetTrainingJobErrorInfoResponseBodyErrorInfo() - self.error_info = temp_model.from_map(m['ErrorInfo']) - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - return self - - -class GetTrainingJobErrorInfoResponse(TeaModel): - def __init__( - self, - headers: Dict[str, str] = None, - status_code: int = None, - body: GetTrainingJobErrorInfoResponseBody = None, - ): - self.headers = headers - self.status_code = status_code - self.body = body - - def validate(self): - if self.body: - self.body.validate() - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.headers is not None: - result['headers'] = self.headers - if self.status_code is not None: - result['statusCode'] = self.status_code - if self.body is not None: - result['body'] = self.body.to_map() - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('headers') is not None: - self.headers = m.get('headers') - if m.get('statusCode') is not None: - self.status_code = m.get('statusCode') - if m.get('body') is not None: - temp_model = GetTrainingJobErrorInfoResponseBody() - self.body = temp_model.from_map(m['body']) - return self - - -class GetTrainingJobLatestMetricsRequest(TeaModel): - def __init__( - self, - names: str = None, - token: str = None, - ): - self.names = names - self.token = token - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.names is not None: - result['Names'] = self.names - if self.token is not None: - result['Token'] = self.token - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('Names') is not None: - self.names = m.get('Names') - if m.get('Token') is not None: - self.token = m.get('Token') - return self - - -class GetTrainingJobLatestMetricsResponseBodyMetrics(TeaModel): - def __init__( - self, - name: str = None, - timestamp: str = None, - value: float = None, - ): - self.name = name - self.timestamp = timestamp - self.value = value - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.name is not None: - result['Name'] = self.name - if self.timestamp is not None: - result['Timestamp'] = self.timestamp - if self.value is not None: - result['Value'] = self.value - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('Name') is not None: - self.name = m.get('Name') - if m.get('Timestamp') is not None: - self.timestamp = m.get('Timestamp') - if m.get('Value') is not None: - self.value = m.get('Value') - return self - - -class GetTrainingJobLatestMetricsResponseBody(TeaModel): - def __init__( - self, - metrics: List[GetTrainingJobLatestMetricsResponseBodyMetrics] = None, - request_id: str = None, - ): - self.metrics = metrics - self.request_id = request_id - - def validate(self): - if self.metrics: - for k in self.metrics: - if k: - k.validate() - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - result['Metrics'] = [] - if self.metrics is not None: - for k in self.metrics: - result['Metrics'].append(k.to_map() if k else None) - if self.request_id is not None: - result['RequestId'] = self.request_id - return result - - def from_map(self, m: dict = None): - m = m or dict() - self.metrics = [] - if m.get('Metrics') is not None: - for k in m.get('Metrics'): - temp_model = GetTrainingJobLatestMetricsResponseBodyMetrics() - self.metrics.append(temp_model.from_map(k)) - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - return self - - -class GetTrainingJobLatestMetricsResponse(TeaModel): - def __init__( - self, - headers: Dict[str, str] = None, - status_code: int = None, - body: GetTrainingJobLatestMetricsResponseBody = None, - ): - self.headers = headers - self.status_code = status_code - self.body = body - - def validate(self): - if self.body: - self.body.validate() - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.headers is not None: - result['headers'] = self.headers - if self.status_code is not None: - result['statusCode'] = self.status_code - if self.body is not None: - result['body'] = self.body.to_map() - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('headers') is not None: - self.headers = m.get('headers') - if m.get('statusCode') is not None: - self.status_code = m.get('statusCode') - if m.get('body') is not None: - temp_model = GetTrainingJobLatestMetricsResponseBody() - self.body = temp_model.from_map(m['body']) - return self - - -class GetUserViewMetricsRequest(TeaModel): - def __init__( - self, - order: str = None, - page_number: str = None, - page_size: str = None, - sort_by: str = None, - time_step: str = None, - user_id: str = None, - workspace_id: str = None, - ): - self.order = order - self.page_number = page_number - self.page_size = page_size - self.sort_by = sort_by - self.time_step = time_step - self.user_id = user_id - self.workspace_id = workspace_id - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.order is not None: - result['Order'] = self.order - if self.page_number is not None: - result['PageNumber'] = self.page_number - if self.page_size is not None: - result['PageSize'] = self.page_size - if self.sort_by is not None: - result['SortBy'] = self.sort_by - if self.time_step is not None: - result['TimeStep'] = self.time_step + if self.algorithm_id is not None: + result['AlgorithmId'] = self.algorithm_id + if self.algorithm_name is not None: + result['AlgorithmName'] = self.algorithm_name + if self.algorithm_provider is not None: + result['AlgorithmProvider'] = self.algorithm_provider + if self.algorithm_spec is not None: + result['AlgorithmSpec'] = self.algorithm_spec.to_map() + if self.algorithm_version is not None: + result['AlgorithmVersion'] = self.algorithm_version + if self.compute_resource is not None: + result['ComputeResource'] = self.compute_resource.to_map() + if self.duration is not None: + result['Duration'] = self.duration + if self.environments is not None: + result['Environments'] = self.environments + if self.experiment_config is not None: + result['ExperimentConfig'] = self.experiment_config.to_map() + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + result['HyperParameters'] = [] + if self.hyper_parameters is not None: + for k in self.hyper_parameters: + result['HyperParameters'].append(k.to_map() if k else None) + result['InputChannels'] = [] + if self.input_channels is not None: + for k in self.input_channels: + result['InputChannels'].append(k.to_map() if k else None) + result['Instances'] = [] + if self.instances is not None: + for k in self.instances: + result['Instances'].append(k.to_map() if k else None) + if self.is_temp_algo is not None: + result['IsTempAlgo'] = self.is_temp_algo + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + result['LatestMetrics'] = [] + if self.latest_metrics is not None: + for k in self.latest_metrics: + result['LatestMetrics'].append(k.to_map() if k else None) + if self.latest_progress is not None: + result['LatestProgress'] = self.latest_progress.to_map() + result['OutputChannels'] = [] + if self.output_channels is not None: + for k in self.output_channels: + result['OutputChannels'].append(k.to_map() if k else None) + if self.output_model is not None: + result['OutputModel'] = self.output_model.to_map() + if self.python_requirements is not None: + result['PythonRequirements'] = self.python_requirements + if self.reason_code is not None: + result['ReasonCode'] = self.reason_code + if self.reason_message is not None: + result['ReasonMessage'] = self.reason_message + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.role_arn is not None: + result['RoleArn'] = self.role_arn + if self.scheduler is not None: + result['Scheduler'] = self.scheduler.to_map() + if self.settings is not None: + result['Settings'] = self.settings.to_map() + if self.status is not None: + result['Status'] = self.status + result['StatusTransitions'] = [] + if self.status_transitions is not None: + for k in self.status_transitions: + result['StatusTransitions'].append(k.to_map() if k else None) + if self.training_job_description is not None: + result['TrainingJobDescription'] = self.training_job_description + if self.training_job_id is not None: + result['TrainingJobId'] = self.training_job_id + if self.training_job_name is not None: + result['TrainingJobName'] = self.training_job_name + if self.training_job_url is not None: + result['TrainingJobUrl'] = self.training_job_url if self.user_id is not None: result['UserId'] = self.user_id + if self.user_vpc is not None: + result['UserVpc'] = self.user_vpc.to_map() if self.workspace_id is not None: result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Order') is not None: - self.order = m.get('Order') - if m.get('PageNumber') is not None: - self.page_number = m.get('PageNumber') - if m.get('PageSize') is not None: - self.page_size = m.get('PageSize') - if m.get('SortBy') is not None: - self.sort_by = m.get('SortBy') - if m.get('TimeStep') is not None: - self.time_step = m.get('TimeStep') - if m.get('UserId') is not None: - self.user_id = m.get('UserId') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') - return self - - -class GetUserViewMetricsResponseBody(TeaModel): - def __init__( - self, - resource_group_id: str = None, - summary: UserViewMetric = None, - total: int = None, - user_metrics: List[UserViewMetric] = None, - ): - self.resource_group_id = resource_group_id - self.summary = summary - self.total = total - self.user_metrics = user_metrics - - def validate(self): - if self.summary: - self.summary.validate() - if self.user_metrics: - for k in self.user_metrics: - if k: - k.validate() - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.resource_group_id is not None: - result['ResourceGroupId'] = self.resource_group_id - if self.summary is not None: - result['Summary'] = self.summary.to_map() - if self.total is not None: - result['Total'] = self.total - result['UserMetrics'] = [] - if self.user_metrics is not None: - for k in self.user_metrics: - result['UserMetrics'].append(k.to_map() if k else None) - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('ResourceGroupId') is not None: - self.resource_group_id = m.get('ResourceGroupId') - if m.get('Summary') is not None: - temp_model = UserViewMetric() - self.summary = temp_model.from_map(m['Summary']) - if m.get('Total') is not None: - self.total = m.get('Total') - self.user_metrics = [] - if m.get('UserMetrics') is not None: - for k in m.get('UserMetrics'): - temp_model = UserViewMetric() - self.user_metrics.append(temp_model.from_map(k)) + if m.get('AlgorithmId') is not None: + self.algorithm_id = m.get('AlgorithmId') + if m.get('AlgorithmName') is not None: + self.algorithm_name = m.get('AlgorithmName') + if m.get('AlgorithmProvider') is not None: + self.algorithm_provider = m.get('AlgorithmProvider') + if m.get('AlgorithmSpec') is not None: + temp_model = AlgorithmSpec() + self.algorithm_spec = temp_model.from_map(m['AlgorithmSpec']) + if m.get('AlgorithmVersion') is not None: + self.algorithm_version = m.get('AlgorithmVersion') + if m.get('ComputeResource') is not None: + temp_model = GetTrainingJobResponseBodyComputeResource() + self.compute_resource = temp_model.from_map(m['ComputeResource']) + if m.get('Duration') is not None: + self.duration = m.get('Duration') + if m.get('Environments') is not None: + self.environments = m.get('Environments') + if m.get('ExperimentConfig') is not None: + temp_model = GetTrainingJobResponseBodyExperimentConfig() + self.experiment_config = temp_model.from_map(m['ExperimentConfig']) + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + self.hyper_parameters = [] + if m.get('HyperParameters') is not None: + for k in m.get('HyperParameters'): + temp_model = GetTrainingJobResponseBodyHyperParameters() + self.hyper_parameters.append(temp_model.from_map(k)) + self.input_channels = [] + if m.get('InputChannels') is not None: + for k in m.get('InputChannels'): + temp_model = GetTrainingJobResponseBodyInputChannels() + self.input_channels.append(temp_model.from_map(k)) + self.instances = [] + if m.get('Instances') is not None: + for k in m.get('Instances'): + temp_model = GetTrainingJobResponseBodyInstances() + self.instances.append(temp_model.from_map(k)) + if m.get('IsTempAlgo') is not None: + self.is_temp_algo = m.get('IsTempAlgo') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = GetTrainingJobResponseBodyLabels() + self.labels.append(temp_model.from_map(k)) + self.latest_metrics = [] + if m.get('LatestMetrics') is not None: + for k in m.get('LatestMetrics'): + temp_model = GetTrainingJobResponseBodyLatestMetrics() + self.latest_metrics.append(temp_model.from_map(k)) + if m.get('LatestProgress') is not None: + temp_model = GetTrainingJobResponseBodyLatestProgress() + self.latest_progress = temp_model.from_map(m['LatestProgress']) + self.output_channels = [] + if m.get('OutputChannels') is not None: + for k in m.get('OutputChannels'): + temp_model = GetTrainingJobResponseBodyOutputChannels() + self.output_channels.append(temp_model.from_map(k)) + if m.get('OutputModel') is not None: + temp_model = GetTrainingJobResponseBodyOutputModel() + self.output_model = temp_model.from_map(m['OutputModel']) + if m.get('PythonRequirements') is not None: + self.python_requirements = m.get('PythonRequirements') + if m.get('ReasonCode') is not None: + self.reason_code = m.get('ReasonCode') + if m.get('ReasonMessage') is not None: + self.reason_message = m.get('ReasonMessage') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('RoleArn') is not None: + self.role_arn = m.get('RoleArn') + if m.get('Scheduler') is not None: + temp_model = GetTrainingJobResponseBodyScheduler() + self.scheduler = temp_model.from_map(m['Scheduler']) + if m.get('Settings') is not None: + temp_model = GetTrainingJobResponseBodySettings() + self.settings = temp_model.from_map(m['Settings']) + if m.get('Status') is not None: + self.status = m.get('Status') + self.status_transitions = [] + if m.get('StatusTransitions') is not None: + for k in m.get('StatusTransitions'): + temp_model = GetTrainingJobResponseBodyStatusTransitions() + self.status_transitions.append(temp_model.from_map(k)) + if m.get('TrainingJobDescription') is not None: + self.training_job_description = m.get('TrainingJobDescription') + if m.get('TrainingJobId') is not None: + self.training_job_id = m.get('TrainingJobId') + if m.get('TrainingJobName') is not None: + self.training_job_name = m.get('TrainingJobName') + if m.get('TrainingJobUrl') is not None: + self.training_job_url = m.get('TrainingJobUrl') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + if m.get('UserVpc') is not None: + temp_model = GetTrainingJobResponseBodyUserVpc() + self.user_vpc = temp_model.from_map(m['UserVpc']) + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class GetUserViewMetricsResponse(TeaModel): +class GetTrainingJobResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: GetUserViewMetricsResponseBody = None, + body: GetTrainingJobResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -15010,19 +13859,17 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = GetUserViewMetricsResponseBody() + temp_model = GetTrainingJobResponseBody() self.body = temp_model.from_map(m['body']) return self -class ListAI4DSerivcesRequest(TeaModel): +class GetTrainingJobErrorInfoRequest(TeaModel): def __init__( self, - service_type: str = None, - workspace_id: str = None, + token: str = None, ): - self.service_type = service_type - self.workspace_id = workspace_id + self.token = token def validate(self): pass @@ -15033,29 +13880,27 @@ def to_map(self): return _map result = dict() - if self.service_type is not None: - result['ServiceType'] = self.service_type - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id + if self.token is not None: + result['Token'] = self.token return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ServiceType') is not None: - self.service_type = m.get('ServiceType') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') + if m.get('Token') is not None: + self.token = m.get('Token') return self -class ListAI4DSerivcesResponseBodyServices(TeaModel): +class GetTrainingJobErrorInfoResponseBodyErrorInfo(TeaModel): def __init__( self, - service_name: str = None, - service_type: str = None, + additional_info: str = None, + code: str = None, + message: str = None, ): - self.service_name = service_name - self.service_type = service_type + self.additional_info = additional_info + self.code = code + self.message = message def validate(self): pass @@ -15066,35 +13911,37 @@ def to_map(self): return _map result = dict() - if self.service_name is not None: - result['ServiceName'] = self.service_name - if self.service_type is not None: - result['ServiceType'] = self.service_type + if self.additional_info is not None: + result['AdditionalInfo'] = self.additional_info + if self.code is not None: + result['Code'] = self.code + if self.message is not None: + result['Message'] = self.message return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ServiceName') is not None: - self.service_name = m.get('ServiceName') - if m.get('ServiceType') is not None: - self.service_type = m.get('ServiceType') + if m.get('AdditionalInfo') is not None: + self.additional_info = m.get('AdditionalInfo') + if m.get('Code') is not None: + self.code = m.get('Code') + if m.get('Message') is not None: + self.message = m.get('Message') return self -class ListAI4DSerivcesResponseBody(TeaModel): +class GetTrainingJobErrorInfoResponseBody(TeaModel): def __init__( self, + error_info: GetTrainingJobErrorInfoResponseBodyErrorInfo = None, request_id: str = None, - services: List[ListAI4DSerivcesResponseBodyServices] = None, ): + self.error_info = error_info self.request_id = request_id - self.services = services def validate(self): - if self.services: - for k in self.services: - if k: - k.validate() + if self.error_info: + self.error_info.validate() def to_map(self): _map = super().to_map() @@ -15102,32 +13949,28 @@ def to_map(self): return _map result = dict() + if self.error_info is not None: + result['ErrorInfo'] = self.error_info.to_map() if self.request_id is not None: result['RequestId'] = self.request_id - result['Services'] = [] - if self.services is not None: - for k in self.services: - result['Services'].append(k.to_map() if k else None) return result def from_map(self, m: dict = None): m = m or dict() + if m.get('ErrorInfo') is not None: + temp_model = GetTrainingJobErrorInfoResponseBodyErrorInfo() + self.error_info = temp_model.from_map(m['ErrorInfo']) if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - self.services = [] - if m.get('Services') is not None: - for k in m.get('Services'): - temp_model = ListAI4DSerivcesResponseBodyServices() - self.services.append(temp_model.from_map(k)) return self -class ListAI4DSerivcesResponse(TeaModel): +class GetTrainingJobErrorInfoResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListAI4DSerivcesResponseBody = None, + body: GetTrainingJobErrorInfoResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -15158,19 +14001,19 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListAI4DSerivcesResponseBody() + temp_model = GetTrainingJobErrorInfoResponseBody() self.body = temp_model.from_map(m['body']) return self -class ListAI4DServiceTemplatesRequest(TeaModel): +class GetTrainingJobLatestMetricsRequest(TeaModel): def __init__( self, - service_type: str = None, - workspace_id: str = None, + names: str = None, + token: str = None, ): - self.service_type = service_type - self.workspace_id = workspace_id + self.names = names + self.token = token def validate(self): pass @@ -15181,28 +14024,31 @@ def to_map(self): return _map result = dict() - if self.service_type is not None: - result['ServiceType'] = self.service_type - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id + if self.names is not None: + result['Names'] = self.names + if self.token is not None: + result['Token'] = self.token return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ServiceType') is not None: - self.service_type = m.get('ServiceType') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') + if m.get('Names') is not None: + self.names = m.get('Names') + if m.get('Token') is not None: + self.token = m.get('Token') return self -class ListAI4DServiceTemplatesResponseBodyServiceTemplatesLabels(TeaModel): +class GetTrainingJobLatestMetricsResponseBodyMetrics(TeaModel): def __init__( self, - key: str = None, - value: str = None, + name: str = None, + timestamp: str = None, + value: float = None, ): - self.key = key + self.name = name + # Use the UTC time format: yyyy-MM-ddTHH:mmZ + self.timestamp = timestamp self.value = value def validate(self): @@ -15214,43 +14060,130 @@ def to_map(self): return _map result = dict() - if self.key is not None: - result['Key'] = self.key + if self.name is not None: + result['Name'] = self.name + if self.timestamp is not None: + result['Timestamp'] = self.timestamp if self.value is not None: result['Value'] = self.value return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Key') is not None: - self.key = m.get('Key') + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('Timestamp') is not None: + self.timestamp = m.get('Timestamp') if m.get('Value') is not None: self.value = m.get('Value') return self -class ListAI4DServiceTemplatesResponseBodyServiceTemplates(TeaModel): +class GetTrainingJobLatestMetricsResponseBody(TeaModel): def __init__( self, - inference_spec: Dict[str, Any] = None, - labels: List[ListAI4DServiceTemplatesResponseBodyServiceTemplatesLabels] = None, - service_template_description: str = None, - service_template_doc: str = None, - service_template_id: str = None, - service_template_name: str = None, + metrics: List[GetTrainingJobLatestMetricsResponseBodyMetrics] = None, + request_id: str = None, + ): + self.metrics = metrics + self.request_id = request_id + + def validate(self): + if self.metrics: + for k in self.metrics: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + result['Metrics'] = [] + if self.metrics is not None: + for k in self.metrics: + result['Metrics'].append(k.to_map() if k else None) + if self.request_id is not None: + result['RequestId'] = self.request_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + self.metrics = [] + if m.get('Metrics') is not None: + for k in m.get('Metrics'): + temp_model = GetTrainingJobLatestMetricsResponseBodyMetrics() + self.metrics.append(temp_model.from_map(k)) + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + return self + + +class GetTrainingJobLatestMetricsResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: GetTrainingJobLatestMetricsResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = GetTrainingJobLatestMetricsResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class GetUserViewMetricsRequest(TeaModel): + def __init__( + self, + order: str = None, + page_number: str = None, + page_size: str = None, + sort_by: str = None, + time_step: str = None, + user_id: str = None, + workspace_id: str = None, ): - self.inference_spec = inference_spec - self.labels = labels - self.service_template_description = service_template_description - self.service_template_doc = service_template_doc - self.service_template_id = service_template_id - self.service_template_name = service_template_name + self.order = order + # This parameter is required. + self.page_number = page_number + # This parameter is required. + self.page_size = page_size + self.sort_by = sort_by + self.time_step = time_step + self.user_id = user_id + self.workspace_id = workspace_id def validate(self): - if self.labels: - for k in self.labels: - if k: - k.validate() + pass def to_map(self): _map = super().to_map() @@ -15258,54 +14191,59 @@ def to_map(self): return _map result = dict() - if self.inference_spec is not None: - result['InferenceSpec'] = self.inference_spec - result['Labels'] = [] - if self.labels is not None: - for k in self.labels: - result['Labels'].append(k.to_map() if k else None) - if self.service_template_description is not None: - result['ServiceTemplateDescription'] = self.service_template_description - if self.service_template_doc is not None: - result['ServiceTemplateDoc'] = self.service_template_doc - if self.service_template_id is not None: - result['ServiceTemplateId'] = self.service_template_id - if self.service_template_name is not None: - result['ServiceTemplateName'] = self.service_template_name + if self.order is not None: + result['Order'] = self.order + if self.page_number is not None: + result['PageNumber'] = self.page_number + if self.page_size is not None: + result['PageSize'] = self.page_size + if self.sort_by is not None: + result['SortBy'] = self.sort_by + if self.time_step is not None: + result['TimeStep'] = self.time_step + if self.user_id is not None: + result['UserId'] = self.user_id + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('InferenceSpec') is not None: - self.inference_spec = m.get('InferenceSpec') - self.labels = [] - if m.get('Labels') is not None: - for k in m.get('Labels'): - temp_model = ListAI4DServiceTemplatesResponseBodyServiceTemplatesLabels() - self.labels.append(temp_model.from_map(k)) - if m.get('ServiceTemplateDescription') is not None: - self.service_template_description = m.get('ServiceTemplateDescription') - if m.get('ServiceTemplateDoc') is not None: - self.service_template_doc = m.get('ServiceTemplateDoc') - if m.get('ServiceTemplateId') is not None: - self.service_template_id = m.get('ServiceTemplateId') - if m.get('ServiceTemplateName') is not None: - self.service_template_name = m.get('ServiceTemplateName') + if m.get('Order') is not None: + self.order = m.get('Order') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + if m.get('SortBy') is not None: + self.sort_by = m.get('SortBy') + if m.get('TimeStep') is not None: + self.time_step = m.get('TimeStep') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class ListAI4DServiceTemplatesResponseBody(TeaModel): +class GetUserViewMetricsResponseBody(TeaModel): def __init__( self, - request_id: str = None, - service_templates: List[ListAI4DServiceTemplatesResponseBodyServiceTemplates] = None, + resource_group_id: str = None, + summary: UserViewMetric = None, + total: int = None, + user_metrics: List[UserViewMetric] = None, ): - self.request_id = request_id - self.service_templates = service_templates + self.resource_group_id = resource_group_id + self.summary = summary + self.total = total + self.user_metrics = user_metrics def validate(self): - if self.service_templates: - for k in self.service_templates: + if self.summary: + self.summary.validate() + if self.user_metrics: + for k in self.user_metrics: if k: k.validate() @@ -15315,32 +14253,41 @@ def to_map(self): return _map result = dict() - if self.request_id is not None: - result['RequestId'] = self.request_id - result['ServiceTemplates'] = [] - if self.service_templates is not None: - for k in self.service_templates: - result['ServiceTemplates'].append(k.to_map() if k else None) + if self.resource_group_id is not None: + result['ResourceGroupId'] = self.resource_group_id + if self.summary is not None: + result['Summary'] = self.summary.to_map() + if self.total is not None: + result['Total'] = self.total + result['UserMetrics'] = [] + if self.user_metrics is not None: + for k in self.user_metrics: + result['UserMetrics'].append(k.to_map() if k else None) return result def from_map(self, m: dict = None): m = m or dict() - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - self.service_templates = [] - if m.get('ServiceTemplates') is not None: - for k in m.get('ServiceTemplates'): - temp_model = ListAI4DServiceTemplatesResponseBodyServiceTemplates() - self.service_templates.append(temp_model.from_map(k)) + if m.get('ResourceGroupId') is not None: + self.resource_group_id = m.get('ResourceGroupId') + if m.get('Summary') is not None: + temp_model = UserViewMetric() + self.summary = temp_model.from_map(m['Summary']) + if m.get('Total') is not None: + self.total = m.get('Total') + self.user_metrics = [] + if m.get('UserMetrics') is not None: + for k in m.get('UserMetrics'): + temp_model = UserViewMetric() + self.user_metrics.append(temp_model.from_map(k)) return self -class ListAI4DServiceTemplatesResponse(TeaModel): +class GetUserViewMetricsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListAI4DServiceTemplatesResponseBody = None, + body: GetUserViewMetricsResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -15371,19 +14318,20 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListAI4DServiceTemplatesResponseBody() + temp_model = GetUserViewMetricsResponseBody() self.body = temp_model.from_map(m['body']) return self -class ListAlgorithmVersionsRequest(TeaModel): +class ListAI4DSerivcesRequest(TeaModel): def __init__( self, - page_number: int = None, - page_size: int = None, + service_type: str = None, + workspace_id: str = None, ): - self.page_number = page_number - self.page_size = page_size + self.service_type = service_type + # This parameter is required. + self.workspace_id = workspace_id def validate(self): pass @@ -15394,41 +14342,29 @@ def to_map(self): return _map result = dict() - if self.page_number is not None: - result['PageNumber'] = self.page_number - if self.page_size is not None: - result['PageSize'] = self.page_size + if self.service_type is not None: + result['ServiceType'] = self.service_type + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('PageNumber') is not None: - self.page_number = m.get('PageNumber') - if m.get('PageSize') is not None: - self.page_size = m.get('PageSize') + if m.get('ServiceType') is not None: + self.service_type = m.get('ServiceType') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class ListAlgorithmVersionsResponseBodyAlgorithmVersions(TeaModel): +class ListAI4DSerivcesResponseBodyServices(TeaModel): def __init__( self, - algorithm_id: str = None, - algorithm_name: str = None, - algorithm_provider: str = None, - algorithm_version: str = None, - gmt_create_time: str = None, - gmt_modified_time: str = None, - tenant_id: str = None, - user_id: str = None, + service_name: str = None, + service_type: str = None, ): - self.algorithm_id = algorithm_id - self.algorithm_name = algorithm_name - self.algorithm_provider = algorithm_provider - self.algorithm_version = algorithm_version - self.gmt_create_time = gmt_create_time - self.gmt_modified_time = gmt_modified_time - self.tenant_id = tenant_id - self.user_id = user_id + self.service_name = service_name + self.service_type = service_type def validate(self): pass @@ -15439,59 +14375,33 @@ def to_map(self): return _map result = dict() - if self.algorithm_id is not None: - result['AlgorithmId'] = self.algorithm_id - if self.algorithm_name is not None: - result['AlgorithmName'] = self.algorithm_name - if self.algorithm_provider is not None: - result['AlgorithmProvider'] = self.algorithm_provider - if self.algorithm_version is not None: - result['AlgorithmVersion'] = self.algorithm_version - if self.gmt_create_time is not None: - result['GmtCreateTime'] = self.gmt_create_time - if self.gmt_modified_time is not None: - result['GmtModifiedTime'] = self.gmt_modified_time - if self.tenant_id is not None: - result['TenantId'] = self.tenant_id - if self.user_id is not None: - result['UserId'] = self.user_id + if self.service_name is not None: + result['ServiceName'] = self.service_name + if self.service_type is not None: + result['ServiceType'] = self.service_type return result def from_map(self, m: dict = None): m = m or dict() - if m.get('AlgorithmId') is not None: - self.algorithm_id = m.get('AlgorithmId') - if m.get('AlgorithmName') is not None: - self.algorithm_name = m.get('AlgorithmName') - if m.get('AlgorithmProvider') is not None: - self.algorithm_provider = m.get('AlgorithmProvider') - if m.get('AlgorithmVersion') is not None: - self.algorithm_version = m.get('AlgorithmVersion') - if m.get('GmtCreateTime') is not None: - self.gmt_create_time = m.get('GmtCreateTime') - if m.get('GmtModifiedTime') is not None: - self.gmt_modified_time = m.get('GmtModifiedTime') - if m.get('TenantId') is not None: - self.tenant_id = m.get('TenantId') - if m.get('UserId') is not None: - self.user_id = m.get('UserId') + if m.get('ServiceName') is not None: + self.service_name = m.get('ServiceName') + if m.get('ServiceType') is not None: + self.service_type = m.get('ServiceType') return self -class ListAlgorithmVersionsResponseBody(TeaModel): +class ListAI4DSerivcesResponseBody(TeaModel): def __init__( self, - algorithm_versions: List[ListAlgorithmVersionsResponseBodyAlgorithmVersions] = None, request_id: str = None, - total_count: int = None, + services: List[ListAI4DSerivcesResponseBodyServices] = None, ): - self.algorithm_versions = algorithm_versions self.request_id = request_id - self.total_count = total_count + self.services = services def validate(self): - if self.algorithm_versions: - for k in self.algorithm_versions: + if self.services: + for k in self.services: if k: k.validate() @@ -15501,36 +14411,32 @@ def to_map(self): return _map result = dict() - result['AlgorithmVersions'] = [] - if self.algorithm_versions is not None: - for k in self.algorithm_versions: - result['AlgorithmVersions'].append(k.to_map() if k else None) if self.request_id is not None: result['RequestId'] = self.request_id - if self.total_count is not None: - result['TotalCount'] = self.total_count + result['Services'] = [] + if self.services is not None: + for k in self.services: + result['Services'].append(k.to_map() if k else None) return result - - def from_map(self, m: dict = None): - m = m or dict() - self.algorithm_versions = [] - if m.get('AlgorithmVersions') is not None: - for k in m.get('AlgorithmVersions'): - temp_model = ListAlgorithmVersionsResponseBodyAlgorithmVersions() - self.algorithm_versions.append(temp_model.from_map(k)) + + def from_map(self, m: dict = None): + m = m or dict() if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('TotalCount') is not None: - self.total_count = m.get('TotalCount') + self.services = [] + if m.get('Services') is not None: + for k in m.get('Services'): + temp_model = ListAI4DSerivcesResponseBodyServices() + self.services.append(temp_model.from_map(k)) return self -class ListAlgorithmVersionsResponse(TeaModel): +class ListAI4DSerivcesResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListAlgorithmVersionsResponseBody = None, + body: ListAI4DSerivcesResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -15561,26 +14467,19 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListAlgorithmVersionsResponseBody() + temp_model = ListAI4DSerivcesResponseBody() self.body = temp_model.from_map(m['body']) return self -class ListAlgorithmsRequest(TeaModel): +class ListAI4DServiceTemplatesRequest(TeaModel): def __init__( self, - algorithm_id: str = None, - algorithm_name: str = None, - algorithm_provider: str = None, - page_number: int = None, - page_size: int = None, + service_type: str = None, workspace_id: str = None, ): - self.algorithm_id = algorithm_id - self.algorithm_name = algorithm_name - self.algorithm_provider = algorithm_provider - self.page_number = page_number - self.page_size = page_size + self.service_type = service_type + # This parameter is required. self.workspace_id = workspace_id def validate(self): @@ -15592,59 +14491,29 @@ def to_map(self): return _map result = dict() - if self.algorithm_id is not None: - result['AlgorithmId'] = self.algorithm_id - if self.algorithm_name is not None: - result['AlgorithmName'] = self.algorithm_name - if self.algorithm_provider is not None: - result['AlgorithmProvider'] = self.algorithm_provider - if self.page_number is not None: - result['PageNumber'] = self.page_number - if self.page_size is not None: - result['PageSize'] = self.page_size + if self.service_type is not None: + result['ServiceType'] = self.service_type if self.workspace_id is not None: result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('AlgorithmId') is not None: - self.algorithm_id = m.get('AlgorithmId') - if m.get('AlgorithmName') is not None: - self.algorithm_name = m.get('AlgorithmName') - if m.get('AlgorithmProvider') is not None: - self.algorithm_provider = m.get('AlgorithmProvider') - if m.get('PageNumber') is not None: - self.page_number = m.get('PageNumber') - if m.get('PageSize') is not None: - self.page_size = m.get('PageSize') + if m.get('ServiceType') is not None: + self.service_type = m.get('ServiceType') if m.get('WorkspaceId') is not None: self.workspace_id = m.get('WorkspaceId') return self -class ListAlgorithmsResponseBodyAlgorithms(TeaModel): +class ListAI4DServiceTemplatesResponseBodyServiceTemplatesLabels(TeaModel): def __init__( self, - algorithm_description: str = None, - algorithm_id: str = None, - algorithm_name: str = None, - algorithm_provider: str = None, - display_name: str = None, - gmt_create_time: str = None, - gmt_modified_time: str = None, - user_id: str = None, - workspace_id: str = None, + key: str = None, + value: str = None, ): - self.algorithm_description = algorithm_description - self.algorithm_id = algorithm_id - self.algorithm_name = algorithm_name - self.algorithm_provider = algorithm_provider - self.display_name = display_name - self.gmt_create_time = gmt_create_time - self.gmt_modified_time = gmt_modified_time - self.user_id = user_id - self.workspace_id = workspace_id + self.key = key + self.value = value def validate(self): pass @@ -15655,63 +14524,98 @@ def to_map(self): return _map result = dict() - if self.algorithm_description is not None: - result['AlgorithmDescription'] = self.algorithm_description - if self.algorithm_id is not None: - result['AlgorithmId'] = self.algorithm_id - if self.algorithm_name is not None: - result['AlgorithmName'] = self.algorithm_name - if self.algorithm_provider is not None: - result['AlgorithmProvider'] = self.algorithm_provider - if self.display_name is not None: - result['DisplayName'] = self.display_name - if self.gmt_create_time is not None: - result['GmtCreateTime'] = self.gmt_create_time - if self.gmt_modified_time is not None: - result['GmtModifiedTime'] = self.gmt_modified_time - if self.user_id is not None: - result['UserId'] = self.user_id - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id + if self.key is not None: + result['Key'] = self.key + if self.value is not None: + result['Value'] = self.value return result def from_map(self, m: dict = None): m = m or dict() - if m.get('AlgorithmDescription') is not None: - self.algorithm_description = m.get('AlgorithmDescription') - if m.get('AlgorithmId') is not None: - self.algorithm_id = m.get('AlgorithmId') - if m.get('AlgorithmName') is not None: - self.algorithm_name = m.get('AlgorithmName') - if m.get('AlgorithmProvider') is not None: - self.algorithm_provider = m.get('AlgorithmProvider') - if m.get('DisplayName') is not None: - self.display_name = m.get('DisplayName') - if m.get('GmtCreateTime') is not None: - self.gmt_create_time = m.get('GmtCreateTime') - if m.get('GmtModifiedTime') is not None: - self.gmt_modified_time = m.get('GmtModifiedTime') - if m.get('UserId') is not None: - self.user_id = m.get('UserId') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') + if m.get('Key') is not None: + self.key = m.get('Key') + if m.get('Value') is not None: + self.value = m.get('Value') return self -class ListAlgorithmsResponseBody(TeaModel): +class ListAI4DServiceTemplatesResponseBodyServiceTemplates(TeaModel): + def __init__( + self, + inference_spec: Dict[str, Any] = None, + labels: List[ListAI4DServiceTemplatesResponseBodyServiceTemplatesLabels] = None, + service_template_description: str = None, + service_template_doc: str = None, + service_template_id: str = None, + service_template_name: str = None, + ): + self.inference_spec = inference_spec + self.labels = labels + self.service_template_description = service_template_description + self.service_template_doc = service_template_doc + self.service_template_id = service_template_id + self.service_template_name = service_template_name + + def validate(self): + if self.labels: + for k in self.labels: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.inference_spec is not None: + result['InferenceSpec'] = self.inference_spec + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + if self.service_template_description is not None: + result['ServiceTemplateDescription'] = self.service_template_description + if self.service_template_doc is not None: + result['ServiceTemplateDoc'] = self.service_template_doc + if self.service_template_id is not None: + result['ServiceTemplateId'] = self.service_template_id + if self.service_template_name is not None: + result['ServiceTemplateName'] = self.service_template_name + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('InferenceSpec') is not None: + self.inference_spec = m.get('InferenceSpec') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = ListAI4DServiceTemplatesResponseBodyServiceTemplatesLabels() + self.labels.append(temp_model.from_map(k)) + if m.get('ServiceTemplateDescription') is not None: + self.service_template_description = m.get('ServiceTemplateDescription') + if m.get('ServiceTemplateDoc') is not None: + self.service_template_doc = m.get('ServiceTemplateDoc') + if m.get('ServiceTemplateId') is not None: + self.service_template_id = m.get('ServiceTemplateId') + if m.get('ServiceTemplateName') is not None: + self.service_template_name = m.get('ServiceTemplateName') + return self + + +class ListAI4DServiceTemplatesResponseBody(TeaModel): def __init__( self, - algorithms: List[ListAlgorithmsResponseBodyAlgorithms] = None, request_id: str = None, - total_count: int = None, + service_templates: List[ListAI4DServiceTemplatesResponseBodyServiceTemplates] = None, ): - self.algorithms = algorithms self.request_id = request_id - self.total_count = total_count + self.service_templates = service_templates def validate(self): - if self.algorithms: - for k in self.algorithms: + if self.service_templates: + for k in self.service_templates: if k: k.validate() @@ -15721,36 +14625,32 @@ def to_map(self): return _map result = dict() - result['Algorithms'] = [] - if self.algorithms is not None: - for k in self.algorithms: - result['Algorithms'].append(k.to_map() if k else None) if self.request_id is not None: result['RequestId'] = self.request_id - if self.total_count is not None: - result['TotalCount'] = self.total_count + result['ServiceTemplates'] = [] + if self.service_templates is not None: + for k in self.service_templates: + result['ServiceTemplates'].append(k.to_map() if k else None) return result def from_map(self, m: dict = None): m = m or dict() - self.algorithms = [] - if m.get('Algorithms') is not None: - for k in m.get('Algorithms'): - temp_model = ListAlgorithmsResponseBodyAlgorithms() - self.algorithms.append(temp_model.from_map(k)) if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - if m.get('TotalCount') is not None: - self.total_count = m.get('TotalCount') + self.service_templates = [] + if m.get('ServiceTemplates') is not None: + for k in m.get('ServiceTemplates'): + temp_model = ListAI4DServiceTemplatesResponseBodyServiceTemplates() + self.service_templates.append(temp_model.from_map(k)) return self -class ListAlgorithmsResponse(TeaModel): +class ListAI4DServiceTemplatesResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListAlgorithmsResponseBody = None, + body: ListAI4DServiceTemplatesResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -15781,29 +14681,19 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListAlgorithmsResponseBody() + temp_model = ListAI4DServiceTemplatesResponseBody() self.body = temp_model.from_map(m['body']) return self -class ListComponentVersionSnapshotsRequest(TeaModel): +class ListAlgorithmVersionsRequest(TeaModel): def __init__( self, - component_id: str = None, - order: str = None, page_number: int = None, page_size: int = None, - snapshot_id: str = None, - sort_by: str = None, - version: str = None, ): - self.component_id = component_id - self.order = order self.page_number = page_number self.page_size = page_size - self.snapshot_id = snapshot_id - self.sort_by = sort_by - self.version = version def validate(self): pass @@ -15814,61 +14704,41 @@ def to_map(self): return _map result = dict() - if self.component_id is not None: - result['ComponentId'] = self.component_id - if self.order is not None: - result['Order'] = self.order if self.page_number is not None: result['PageNumber'] = self.page_number if self.page_size is not None: result['PageSize'] = self.page_size - if self.snapshot_id is not None: - result['SnapshotId'] = self.snapshot_id - if self.sort_by is not None: - result['SortBy'] = self.sort_by - if self.version is not None: - result['Version'] = self.version return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('ComponentId') is not None: - self.component_id = m.get('ComponentId') - if m.get('Order') is not None: - self.order = m.get('Order') + + def from_map(self, m: dict = None): + m = m or dict() if m.get('PageNumber') is not None: self.page_number = m.get('PageNumber') if m.get('PageSize') is not None: self.page_size = m.get('PageSize') - if m.get('SnapshotId') is not None: - self.snapshot_id = m.get('SnapshotId') - if m.get('SortBy') is not None: - self.sort_by = m.get('SortBy') - if m.get('Version') is not None: - self.version = m.get('Version') return self -class ListComponentVersionSnapshotsResponseBodySnapshots(TeaModel): +class ListAlgorithmVersionsResponseBodyAlgorithmVersions(TeaModel): def __init__( self, - component_id: str = None, - description: str = None, - is_current_version: bool = None, - snapshot_id: str = None, + algorithm_id: str = None, + algorithm_name: str = None, + algorithm_provider: str = None, + algorithm_version: str = None, + gmt_create_time: str = None, + gmt_modified_time: str = None, tenant_id: str = None, user_id: str = None, - version: str = None, - workspace_id: str = None, ): - self.component_id = component_id - self.description = description - self.is_current_version = is_current_version - self.snapshot_id = snapshot_id + self.algorithm_id = algorithm_id + self.algorithm_name = algorithm_name + self.algorithm_provider = algorithm_provider + self.algorithm_version = algorithm_version + self.gmt_create_time = gmt_create_time + self.gmt_modified_time = gmt_modified_time self.tenant_id = tenant_id self.user_id = user_id - self.version = version - self.workspace_id = workspace_id def validate(self): pass @@ -15879,59 +14749,59 @@ def to_map(self): return _map result = dict() - if self.component_id is not None: - result['ComponentId'] = self.component_id - if self.description is not None: - result['Description'] = self.description - if self.is_current_version is not None: - result['IsCurrentVersion'] = self.is_current_version - if self.snapshot_id is not None: - result['SnapshotId'] = self.snapshot_id + if self.algorithm_id is not None: + result['AlgorithmId'] = self.algorithm_id + if self.algorithm_name is not None: + result['AlgorithmName'] = self.algorithm_name + if self.algorithm_provider is not None: + result['AlgorithmProvider'] = self.algorithm_provider + if self.algorithm_version is not None: + result['AlgorithmVersion'] = self.algorithm_version + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time if self.tenant_id is not None: result['TenantId'] = self.tenant_id if self.user_id is not None: result['UserId'] = self.user_id - if self.version is not None: - result['Version'] = self.version - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ComponentId') is not None: - self.component_id = m.get('ComponentId') - if m.get('Description') is not None: - self.description = m.get('Description') - if m.get('IsCurrentVersion') is not None: - self.is_current_version = m.get('IsCurrentVersion') - if m.get('SnapshotId') is not None: - self.snapshot_id = m.get('SnapshotId') + if m.get('AlgorithmId') is not None: + self.algorithm_id = m.get('AlgorithmId') + if m.get('AlgorithmName') is not None: + self.algorithm_name = m.get('AlgorithmName') + if m.get('AlgorithmProvider') is not None: + self.algorithm_provider = m.get('AlgorithmProvider') + if m.get('AlgorithmVersion') is not None: + self.algorithm_version = m.get('AlgorithmVersion') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') if m.get('TenantId') is not None: self.tenant_id = m.get('TenantId') if m.get('UserId') is not None: self.user_id = m.get('UserId') - if m.get('Version') is not None: - self.version = m.get('Version') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') return self -class ListComponentVersionSnapshotsResponseBody(TeaModel): +class ListAlgorithmVersionsResponseBody(TeaModel): def __init__( self, + algorithm_versions: List[ListAlgorithmVersionsResponseBodyAlgorithmVersions] = None, request_id: str = None, - snapshots: List[ListComponentVersionSnapshotsResponseBodySnapshots] = None, total_count: int = None, ): + self.algorithm_versions = algorithm_versions self.request_id = request_id - self.snapshots = snapshots self.total_count = total_count def validate(self): - if self.snapshots: - for k in self.snapshots: + if self.algorithm_versions: + for k in self.algorithm_versions: if k: k.validate() @@ -15941,36 +14811,36 @@ def to_map(self): return _map result = dict() + result['AlgorithmVersions'] = [] + if self.algorithm_versions is not None: + for k in self.algorithm_versions: + result['AlgorithmVersions'].append(k.to_map() if k else None) if self.request_id is not None: result['RequestId'] = self.request_id - result['Snapshots'] = [] - if self.snapshots is not None: - for k in self.snapshots: - result['Snapshots'].append(k.to_map() if k else None) if self.total_count is not None: result['TotalCount'] = self.total_count return result def from_map(self, m: dict = None): m = m or dict() + self.algorithm_versions = [] + if m.get('AlgorithmVersions') is not None: + for k in m.get('AlgorithmVersions'): + temp_model = ListAlgorithmVersionsResponseBodyAlgorithmVersions() + self.algorithm_versions.append(temp_model.from_map(k)) if m.get('RequestId') is not None: self.request_id = m.get('RequestId') - self.snapshots = [] - if m.get('Snapshots') is not None: - for k in m.get('Snapshots'): - temp_model = ListComponentVersionSnapshotsResponseBodySnapshots() - self.snapshots.append(temp_model.from_map(k)) if m.get('TotalCount') is not None: self.total_count = m.get('TotalCount') return self -class ListComponentVersionSnapshotsResponse(TeaModel): +class ListAlgorithmVersionsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListComponentVersionSnapshotsResponseBody = None, + body: ListAlgorithmVersionsResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -16001,84 +14871,27 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListComponentVersionSnapshotsResponseBody() + temp_model = ListAlgorithmVersionsResponseBody() self.body = temp_model.from_map(m['body']) return self -class ListComponentVersionsRequest(TeaModel): - def __init__( - self, - labels: Dict[str, str] = None, - order: str = None, - page_number: int = None, - page_size: int = None, - sort_by: str = None, - version: str = None, - ): - self.labels = labels - self.order = order - self.page_number = page_number - self.page_size = page_size - self.sort_by = sort_by - self.version = version - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.labels is not None: - result['Labels'] = self.labels - if self.order is not None: - result['Order'] = self.order - if self.page_number is not None: - result['PageNumber'] = self.page_number - if self.page_size is not None: - result['PageSize'] = self.page_size - if self.sort_by is not None: - result['SortBy'] = self.sort_by - if self.version is not None: - result['Version'] = self.version - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('Labels') is not None: - self.labels = m.get('Labels') - if m.get('Order') is not None: - self.order = m.get('Order') - if m.get('PageNumber') is not None: - self.page_number = m.get('PageNumber') - if m.get('PageSize') is not None: - self.page_size = m.get('PageSize') - if m.get('SortBy') is not None: - self.sort_by = m.get('SortBy') - if m.get('Version') is not None: - self.version = m.get('Version') - return self - - -class ListComponentVersionsShrinkRequest(TeaModel): +class ListAlgorithmsRequest(TeaModel): def __init__( self, - labels_shrink: str = None, - order: str = None, + algorithm_id: str = None, + algorithm_name: str = None, + algorithm_provider: str = None, page_number: int = None, page_size: int = None, - sort_by: str = None, - version: str = None, + workspace_id: str = None, ): - self.labels_shrink = labels_shrink - self.order = order + self.algorithm_id = algorithm_id + self.algorithm_name = algorithm_name + self.algorithm_provider = algorithm_provider self.page_number = page_number self.page_size = page_size - self.sort_by = sort_by - self.version = version + self.workspace_id = workspace_id def validate(self): pass @@ -16089,146 +14902,126 @@ def to_map(self): return _map result = dict() - if self.labels_shrink is not None: - result['Labels'] = self.labels_shrink - if self.order is not None: - result['Order'] = self.order + if self.algorithm_id is not None: + result['AlgorithmId'] = self.algorithm_id + if self.algorithm_name is not None: + result['AlgorithmName'] = self.algorithm_name + if self.algorithm_provider is not None: + result['AlgorithmProvider'] = self.algorithm_provider if self.page_number is not None: result['PageNumber'] = self.page_number if self.page_size is not None: result['PageSize'] = self.page_size - if self.sort_by is not None: - result['SortBy'] = self.sort_by - if self.version is not None: - result['Version'] = self.version + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Labels') is not None: - self.labels_shrink = m.get('Labels') - if m.get('Order') is not None: - self.order = m.get('Order') + if m.get('AlgorithmId') is not None: + self.algorithm_id = m.get('AlgorithmId') + if m.get('AlgorithmName') is not None: + self.algorithm_name = m.get('AlgorithmName') + if m.get('AlgorithmProvider') is not None: + self.algorithm_provider = m.get('AlgorithmProvider') if m.get('PageNumber') is not None: self.page_number = m.get('PageNumber') if m.get('PageSize') is not None: self.page_size = m.get('PageSize') - if m.get('SortBy') is not None: - self.sort_by = m.get('SortBy') - if m.get('Version') is not None: - self.version = m.get('Version') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class ListComponentVersionsResponseBodyComponentVersions(TeaModel): +class ListAlgorithmsResponseBodyAlgorithms(TeaModel): def __init__( self, - component_id: str = None, + algorithm_description: str = None, + algorithm_id: str = None, + algorithm_name: str = None, + algorithm_provider: str = None, + display_name: str = None, gmt_create_time: str = None, gmt_modified_time: str = None, - labels: List[Label] = None, - name: str = None, - provider: str = None, - status: str = None, - tenant_id: str = None, user_id: str = None, - version: str = None, workspace_id: str = None, ): - self.component_id = component_id + self.algorithm_description = algorithm_description + self.algorithm_id = algorithm_id + self.algorithm_name = algorithm_name + self.algorithm_provider = algorithm_provider + self.display_name = display_name self.gmt_create_time = gmt_create_time self.gmt_modified_time = gmt_modified_time - self.labels = labels - self.name = name - self.provider = provider - self.status = status - self.tenant_id = tenant_id self.user_id = user_id - self.version = version self.workspace_id = workspace_id - def validate(self): - if self.labels: - for k in self.labels: - if k: - k.validate() - + def validate(self): + pass + def to_map(self): _map = super().to_map() if _map is not None: return _map result = dict() - if self.component_id is not None: - result['ComponentId'] = self.component_id + if self.algorithm_description is not None: + result['AlgorithmDescription'] = self.algorithm_description + if self.algorithm_id is not None: + result['AlgorithmId'] = self.algorithm_id + if self.algorithm_name is not None: + result['AlgorithmName'] = self.algorithm_name + if self.algorithm_provider is not None: + result['AlgorithmProvider'] = self.algorithm_provider + if self.display_name is not None: + result['DisplayName'] = self.display_name if self.gmt_create_time is not None: result['GmtCreateTime'] = self.gmt_create_time if self.gmt_modified_time is not None: result['GmtModifiedTime'] = self.gmt_modified_time - result['Labels'] = [] - if self.labels is not None: - for k in self.labels: - result['Labels'].append(k.to_map() if k else None) - if self.name is not None: - result['Name'] = self.name - if self.provider is not None: - result['Provider'] = self.provider - if self.status is not None: - result['Status'] = self.status - if self.tenant_id is not None: - result['TenantId'] = self.tenant_id if self.user_id is not None: result['UserId'] = self.user_id - if self.version is not None: - result['Version'] = self.version if self.workspace_id is not None: result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ComponentId') is not None: - self.component_id = m.get('ComponentId') + if m.get('AlgorithmDescription') is not None: + self.algorithm_description = m.get('AlgorithmDescription') + if m.get('AlgorithmId') is not None: + self.algorithm_id = m.get('AlgorithmId') + if m.get('AlgorithmName') is not None: + self.algorithm_name = m.get('AlgorithmName') + if m.get('AlgorithmProvider') is not None: + self.algorithm_provider = m.get('AlgorithmProvider') + if m.get('DisplayName') is not None: + self.display_name = m.get('DisplayName') if m.get('GmtCreateTime') is not None: self.gmt_create_time = m.get('GmtCreateTime') if m.get('GmtModifiedTime') is not None: self.gmt_modified_time = m.get('GmtModifiedTime') - self.labels = [] - if m.get('Labels') is not None: - for k in m.get('Labels'): - temp_model = Label() - self.labels.append(temp_model.from_map(k)) - if m.get('Name') is not None: - self.name = m.get('Name') - if m.get('Provider') is not None: - self.provider = m.get('Provider') - if m.get('Status') is not None: - self.status = m.get('Status') - if m.get('TenantId') is not None: - self.tenant_id = m.get('TenantId') if m.get('UserId') is not None: self.user_id = m.get('UserId') - if m.get('Version') is not None: - self.version = m.get('Version') if m.get('WorkspaceId') is not None: self.workspace_id = m.get('WorkspaceId') return self -class ListComponentVersionsResponseBody(TeaModel): +class ListAlgorithmsResponseBody(TeaModel): def __init__( self, - component_versions: List[ListComponentVersionsResponseBodyComponentVersions] = None, + algorithms: List[ListAlgorithmsResponseBodyAlgorithms] = None, request_id: str = None, total_count: int = None, ): - self.component_versions = component_versions + self.algorithms = algorithms self.request_id = request_id self.total_count = total_count def validate(self): - if self.component_versions: - for k in self.component_versions: + if self.algorithms: + for k in self.algorithms: if k: k.validate() @@ -16238,10 +15031,10 @@ def to_map(self): return _map result = dict() - result['ComponentVersions'] = [] - if self.component_versions is not None: - for k in self.component_versions: - result['ComponentVersions'].append(k.to_map() if k else None) + result['Algorithms'] = [] + if self.algorithms is not None: + for k in self.algorithms: + result['Algorithms'].append(k.to_map() if k else None) if self.request_id is not None: result['RequestId'] = self.request_id if self.total_count is not None: @@ -16250,11 +15043,11 @@ def to_map(self): def from_map(self, m: dict = None): m = m or dict() - self.component_versions = [] - if m.get('ComponentVersions') is not None: - for k in m.get('ComponentVersions'): - temp_model = ListComponentVersionsResponseBodyComponentVersions() - self.component_versions.append(temp_model.from_map(k)) + self.algorithms = [] + if m.get('Algorithms') is not None: + for k in m.get('Algorithms'): + temp_model = ListAlgorithmsResponseBodyAlgorithms() + self.algorithms.append(temp_model.from_map(k)) if m.get('RequestId') is not None: self.request_id = m.get('RequestId') if m.get('TotalCount') is not None: @@ -16262,12 +15055,12 @@ def from_map(self, m: dict = None): return self -class ListComponentVersionsResponse(TeaModel): +class ListAlgorithmsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListComponentVersionsResponseBody = None, + body: ListAlgorithmsResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -16298,116 +15091,31 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListComponentVersionsResponseBody() + temp_model = ListAlgorithmsResponseBody() self.body = temp_model.from_map(m['body']) return self -class ListComponentsRequest(TeaModel): - def __init__( - self, - component_id: str = None, - component_ids: str = None, - labels: Dict[str, Any] = None, - name: str = None, - order: str = None, - page_number: int = None, - page_size: int = None, - provider: str = None, - sort_by: str = None, - workspace_id: str = None, - ): - self.component_id = component_id - self.component_ids = component_ids - self.labels = labels - self.name = name - self.order = order - self.page_number = page_number - self.page_size = page_size - self.provider = provider - self.sort_by = sort_by - self.workspace_id = workspace_id - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.component_id is not None: - result['ComponentId'] = self.component_id - if self.component_ids is not None: - result['ComponentIds'] = self.component_ids - if self.labels is not None: - result['Labels'] = self.labels - if self.name is not None: - result['Name'] = self.name - if self.order is not None: - result['Order'] = self.order - if self.page_number is not None: - result['PageNumber'] = self.page_number - if self.page_size is not None: - result['PageSize'] = self.page_size - if self.provider is not None: - result['Provider'] = self.provider - if self.sort_by is not None: - result['SortBy'] = self.sort_by - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('ComponentId') is not None: - self.component_id = m.get('ComponentId') - if m.get('ComponentIds') is not None: - self.component_ids = m.get('ComponentIds') - if m.get('Labels') is not None: - self.labels = m.get('Labels') - if m.get('Name') is not None: - self.name = m.get('Name') - if m.get('Order') is not None: - self.order = m.get('Order') - if m.get('PageNumber') is not None: - self.page_number = m.get('PageNumber') - if m.get('PageSize') is not None: - self.page_size = m.get('PageSize') - if m.get('Provider') is not None: - self.provider = m.get('Provider') - if m.get('SortBy') is not None: - self.sort_by = m.get('SortBy') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') - return self - - -class ListComponentsShrinkRequest(TeaModel): +class ListComponentVersionSnapshotsRequest(TeaModel): def __init__( self, component_id: str = None, - component_ids: str = None, - labels_shrink: str = None, - name: str = None, order: str = None, page_number: int = None, page_size: int = None, - provider: str = None, + snapshot_id: str = None, sort_by: str = None, - workspace_id: str = None, + version: str = None, ): + # This parameter is required. self.component_id = component_id - self.component_ids = component_ids - self.labels_shrink = labels_shrink - self.name = name self.order = order self.page_number = page_number self.page_size = page_size - self.provider = provider + self.snapshot_id = snapshot_id self.sort_by = sort_by - self.workspace_id = workspace_id + # This parameter is required. + self.version = version def validate(self): pass @@ -16420,134 +15128,62 @@ def to_map(self): result = dict() if self.component_id is not None: result['ComponentId'] = self.component_id - if self.component_ids is not None: - result['ComponentIds'] = self.component_ids - if self.labels_shrink is not None: - result['Labels'] = self.labels_shrink - if self.name is not None: - result['Name'] = self.name if self.order is not None: result['Order'] = self.order if self.page_number is not None: result['PageNumber'] = self.page_number if self.page_size is not None: result['PageSize'] = self.page_size - if self.provider is not None: - result['Provider'] = self.provider + if self.snapshot_id is not None: + result['SnapshotId'] = self.snapshot_id if self.sort_by is not None: result['SortBy'] = self.sort_by - if self.workspace_id is not None: - result['WorkspaceId'] = self.workspace_id + if self.version is not None: + result['Version'] = self.version return result def from_map(self, m: dict = None): m = m or dict() if m.get('ComponentId') is not None: self.component_id = m.get('ComponentId') - if m.get('ComponentIds') is not None: - self.component_ids = m.get('ComponentIds') - if m.get('Labels') is not None: - self.labels_shrink = m.get('Labels') - if m.get('Name') is not None: - self.name = m.get('Name') if m.get('Order') is not None: self.order = m.get('Order') - if m.get('PageNumber') is not None: - self.page_number = m.get('PageNumber') - if m.get('PageSize') is not None: - self.page_size = m.get('PageSize') - if m.get('Provider') is not None: - self.provider = m.get('Provider') - if m.get('SortBy') is not None: - self.sort_by = m.get('SortBy') - if m.get('WorkspaceId') is not None: - self.workspace_id = m.get('WorkspaceId') - return self - - -class ListComponentsResponseBodyComponentsVersions(TeaModel): - def __init__( - self, - gmt_create_time: str = None, - snapshot_id: str = None, - status: str = None, - version: str = None, - ): - self.gmt_create_time = gmt_create_time - self.snapshot_id = snapshot_id - self.status = status - self.version = version - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.gmt_create_time is not None: - result['GmtCreateTime'] = self.gmt_create_time - if self.snapshot_id is not None: - result['SnapshotId'] = self.snapshot_id - if self.status is not None: - result['Status'] = self.status - if self.version is not None: - result['Version'] = self.version - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('GmtCreateTime') is not None: - self.gmt_create_time = m.get('GmtCreateTime') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') if m.get('SnapshotId') is not None: self.snapshot_id = m.get('SnapshotId') - if m.get('Status') is not None: - self.status = m.get('Status') + if m.get('SortBy') is not None: + self.sort_by = m.get('SortBy') if m.get('Version') is not None: self.version = m.get('Version') return self -class ListComponentsResponseBodyComponents(TeaModel): +class ListComponentVersionSnapshotsResponseBodySnapshots(TeaModel): def __init__( self, component_id: str = None, description: str = None, - display_name: str = None, - gmt_create_time: str = None, - gmt_modified_time: str = None, - labels: List[Label] = None, - name: str = None, - provider: str = None, + is_current_version: bool = None, + snapshot_id: str = None, tenant_id: str = None, user_id: str = None, - versions: List[ListComponentsResponseBodyComponentsVersions] = None, + version: str = None, workspace_id: str = None, ): self.component_id = component_id self.description = description - self.display_name = display_name - self.gmt_create_time = gmt_create_time - self.gmt_modified_time = gmt_modified_time - self.labels = labels - self.name = name - self.provider = provider + self.is_current_version = is_current_version + self.snapshot_id = snapshot_id self.tenant_id = tenant_id self.user_id = user_id - self.versions = versions + self.version = version self.workspace_id = workspace_id def validate(self): - if self.labels: - for k in self.labels: - if k: - k.validate() - if self.versions: - for k in self.versions: - if k: - k.validate() + pass def to_map(self): _map = super().to_map() @@ -16559,28 +15195,16 @@ def to_map(self): result['ComponentId'] = self.component_id if self.description is not None: result['Description'] = self.description - if self.display_name is not None: - result['DisplayName'] = self.display_name - if self.gmt_create_time is not None: - result['GmtCreateTime'] = self.gmt_create_time - if self.gmt_modified_time is not None: - result['GmtModifiedTime'] = self.gmt_modified_time - result['Labels'] = [] - if self.labels is not None: - for k in self.labels: - result['Labels'].append(k.to_map() if k else None) - if self.name is not None: - result['Name'] = self.name - if self.provider is not None: - result['Provider'] = self.provider + if self.is_current_version is not None: + result['IsCurrentVersion'] = self.is_current_version + if self.snapshot_id is not None: + result['SnapshotId'] = self.snapshot_id if self.tenant_id is not None: result['TenantId'] = self.tenant_id if self.user_id is not None: result['UserId'] = self.user_id - result['Versions'] = [] - if self.versions is not None: - for k in self.versions: - result['Versions'].append(k.to_map() if k else None) + if self.version is not None: + result['Version'] = self.version if self.workspace_id is not None: result['WorkspaceId'] = self.workspace_id return result @@ -16591,49 +15215,35 @@ def from_map(self, m: dict = None): self.component_id = m.get('ComponentId') if m.get('Description') is not None: self.description = m.get('Description') - if m.get('DisplayName') is not None: - self.display_name = m.get('DisplayName') - if m.get('GmtCreateTime') is not None: - self.gmt_create_time = m.get('GmtCreateTime') - if m.get('GmtModifiedTime') is not None: - self.gmt_modified_time = m.get('GmtModifiedTime') - self.labels = [] - if m.get('Labels') is not None: - for k in m.get('Labels'): - temp_model = Label() - self.labels.append(temp_model.from_map(k)) - if m.get('Name') is not None: - self.name = m.get('Name') - if m.get('Provider') is not None: - self.provider = m.get('Provider') + if m.get('IsCurrentVersion') is not None: + self.is_current_version = m.get('IsCurrentVersion') + if m.get('SnapshotId') is not None: + self.snapshot_id = m.get('SnapshotId') if m.get('TenantId') is not None: self.tenant_id = m.get('TenantId') if m.get('UserId') is not None: self.user_id = m.get('UserId') - self.versions = [] - if m.get('Versions') is not None: - for k in m.get('Versions'): - temp_model = ListComponentsResponseBodyComponentsVersions() - self.versions.append(temp_model.from_map(k)) + if m.get('Version') is not None: + self.version = m.get('Version') if m.get('WorkspaceId') is not None: self.workspace_id = m.get('WorkspaceId') return self -class ListComponentsResponseBody(TeaModel): +class ListComponentVersionSnapshotsResponseBody(TeaModel): def __init__( self, - components: List[ListComponentsResponseBodyComponents] = None, request_id: str = None, + snapshots: List[ListComponentVersionSnapshotsResponseBodySnapshots] = None, total_count: int = None, ): - self.components = components self.request_id = request_id + self.snapshots = snapshots self.total_count = total_count def validate(self): - if self.components: - for k in self.components: + if self.snapshots: + for k in self.snapshots: if k: k.validate() @@ -16643,36 +15253,36 @@ def to_map(self): return _map result = dict() - result['Components'] = [] - if self.components is not None: - for k in self.components: - result['Components'].append(k.to_map() if k else None) if self.request_id is not None: result['RequestId'] = self.request_id + result['Snapshots'] = [] + if self.snapshots is not None: + for k in self.snapshots: + result['Snapshots'].append(k.to_map() if k else None) if self.total_count is not None: result['TotalCount'] = self.total_count return result def from_map(self, m: dict = None): m = m or dict() - self.components = [] - if m.get('Components') is not None: - for k in m.get('Components'): - temp_model = ListComponentsResponseBodyComponents() - self.components.append(temp_model.from_map(k)) if m.get('RequestId') is not None: self.request_id = m.get('RequestId') + self.snapshots = [] + if m.get('Snapshots') is not None: + for k in m.get('Snapshots'): + temp_model = ListComponentVersionSnapshotsResponseBodySnapshots() + self.snapshots.append(temp_model.from_map(k)) if m.get('TotalCount') is not None: self.total_count = m.get('TotalCount') return self -class ListComponentsResponse(TeaModel): +class ListComponentVersionSnapshotsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListComponentsResponseBody = None, + body: ListComponentVersionSnapshotsResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -16703,27 +15313,27 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListComponentsResponseBody() + temp_model = ListComponentVersionSnapshotsResponseBody() self.body = temp_model.from_map(m['body']) return self -class ListInstanceJobsRequest(TeaModel): +class ListComponentVersionsRequest(TeaModel): def __init__( self, - instance_job_type: str = None, + labels: Dict[str, str] = None, order: str = None, page_number: int = None, page_size: int = None, sort_by: str = None, - status: str = None, + version: str = None, ): - self.instance_job_type = instance_job_type + self.labels = labels self.order = order self.page_number = page_number self.page_size = page_size self.sort_by = sort_by - self.status = status + self.version = version def validate(self): pass @@ -16734,8 +15344,8 @@ def to_map(self): return _map result = dict() - if self.instance_job_type is not None: - result['InstanceJobType'] = self.instance_job_type + if self.labels is not None: + result['Labels'] = self.labels if self.order is not None: result['Order'] = self.order if self.page_number is not None: @@ -16744,14 +15354,14 @@ def to_map(self): result['PageSize'] = self.page_size if self.sort_by is not None: result['SortBy'] = self.sort_by - if self.status is not None: - result['Status'] = self.status + if self.version is not None: + result['Version'] = self.version return result def from_map(self, m: dict = None): m = m or dict() - if m.get('InstanceJobType') is not None: - self.instance_job_type = m.get('InstanceJobType') + if m.get('Labels') is not None: + self.labels = m.get('Labels') if m.get('Order') is not None: self.order = m.get('Order') if m.get('PageNumber') is not None: @@ -16760,100 +15370,181 @@ def from_map(self, m: dict = None): self.page_size = m.get('PageSize') if m.get('SortBy') is not None: self.sort_by = m.get('SortBy') - if m.get('Status') is not None: - self.status = m.get('Status') + if m.get('Version') is not None: + self.version = m.get('Version') return self -class ListInstanceJobsResponseBodyInstanceJobs(TeaModel): +class ListComponentVersionsShrinkRequest(TeaModel): def __init__( self, - creator: str = None, + labels_shrink: str = None, + order: str = None, + page_number: int = None, + page_size: int = None, + sort_by: str = None, + version: str = None, + ): + self.labels_shrink = labels_shrink + self.order = order + self.page_number = page_number + self.page_size = page_size + self.sort_by = sort_by + self.version = version + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.labels_shrink is not None: + result['Labels'] = self.labels_shrink + if self.order is not None: + result['Order'] = self.order + if self.page_number is not None: + result['PageNumber'] = self.page_number + if self.page_size is not None: + result['PageSize'] = self.page_size + if self.sort_by is not None: + result['SortBy'] = self.sort_by + if self.version is not None: + result['Version'] = self.version + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('Labels') is not None: + self.labels_shrink = m.get('Labels') + if m.get('Order') is not None: + self.order = m.get('Order') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + if m.get('SortBy') is not None: + self.sort_by = m.get('SortBy') + if m.get('Version') is not None: + self.version = m.get('Version') + return self + + +class ListComponentVersionsResponseBodyComponentVersions(TeaModel): + def __init__( + self, + component_id: str = None, gmt_create_time: str = None, - instance_id: str = None, - instance_job_id: str = None, - instance_job_type: str = None, - reason_code: str = None, - reason_message: str = None, + gmt_modified_time: str = None, + labels: List[Label] = None, + name: str = None, + provider: str = None, status: str = None, + tenant_id: str = None, + user_id: str = None, + version: str = None, workspace_id: str = None, ): - self.creator = creator + self.component_id = component_id + # Use the UTC time format: yyyy-MM-ddTHH:mmZ self.gmt_create_time = gmt_create_time - self.instance_id = instance_id - self.instance_job_id = instance_job_id - self.instance_job_type = instance_job_type - self.reason_code = reason_code - self.reason_message = reason_message + # Use the UTC time format: yyyy-MM-ddTHH:mmZ + self.gmt_modified_time = gmt_modified_time + self.labels = labels + self.name = name + self.provider = provider self.status = status + self.tenant_id = tenant_id + self.user_id = user_id + self.version = version self.workspace_id = workspace_id def validate(self): - pass + if self.labels: + for k in self.labels: + if k: + k.validate() def to_map(self): _map = super().to_map() if _map is not None: return _map - result = dict() - if self.creator is not None: - result['Creator'] = self.creator - if self.gmt_create_time is not None: - result['GmtCreateTime'] = self.gmt_create_time - if self.instance_id is not None: - result['InstanceId'] = self.instance_id - if self.instance_job_id is not None: - result['InstanceJobId'] = self.instance_job_id - if self.instance_job_type is not None: - result['InstanceJobType'] = self.instance_job_type - if self.reason_code is not None: - result['ReasonCode'] = self.reason_code - if self.reason_message is not None: - result['ReasonMessage'] = self.reason_message + result = dict() + if self.component_id is not None: + result['ComponentId'] = self.component_id + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.gmt_modified_time is not None: + result['GmtModifiedTime'] = self.gmt_modified_time + result['Labels'] = [] + if self.labels is not None: + for k in self.labels: + result['Labels'].append(k.to_map() if k else None) + if self.name is not None: + result['Name'] = self.name + if self.provider is not None: + result['Provider'] = self.provider if self.status is not None: result['Status'] = self.status + if self.tenant_id is not None: + result['TenantId'] = self.tenant_id + if self.user_id is not None: + result['UserId'] = self.user_id + if self.version is not None: + result['Version'] = self.version if self.workspace_id is not None: result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Creator') is not None: - self.creator = m.get('Creator') + if m.get('ComponentId') is not None: + self.component_id = m.get('ComponentId') if m.get('GmtCreateTime') is not None: self.gmt_create_time = m.get('GmtCreateTime') - if m.get('InstanceId') is not None: - self.instance_id = m.get('InstanceId') - if m.get('InstanceJobId') is not None: - self.instance_job_id = m.get('InstanceJobId') - if m.get('InstanceJobType') is not None: - self.instance_job_type = m.get('InstanceJobType') - if m.get('ReasonCode') is not None: - self.reason_code = m.get('ReasonCode') - if m.get('ReasonMessage') is not None: - self.reason_message = m.get('ReasonMessage') + if m.get('GmtModifiedTime') is not None: + self.gmt_modified_time = m.get('GmtModifiedTime') + self.labels = [] + if m.get('Labels') is not None: + for k in m.get('Labels'): + temp_model = Label() + self.labels.append(temp_model.from_map(k)) + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('Provider') is not None: + self.provider = m.get('Provider') if m.get('Status') is not None: self.status = m.get('Status') + if m.get('TenantId') is not None: + self.tenant_id = m.get('TenantId') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + if m.get('Version') is not None: + self.version = m.get('Version') if m.get('WorkspaceId') is not None: self.workspace_id = m.get('WorkspaceId') return self -class ListInstanceJobsResponseBody(TeaModel): +class ListComponentVersionsResponseBody(TeaModel): def __init__( self, - instance_jobs: ListInstanceJobsResponseBodyInstanceJobs = None, + component_versions: List[ListComponentVersionsResponseBodyComponentVersions] = None, request_id: str = None, total_count: int = None, ): - self.instance_jobs = instance_jobs + self.component_versions = component_versions self.request_id = request_id self.total_count = total_count def validate(self): - if self.instance_jobs: - self.instance_jobs.validate() + if self.component_versions: + for k in self.component_versions: + if k: + k.validate() def to_map(self): _map = super().to_map() @@ -16861,8 +15552,10 @@ def to_map(self): return _map result = dict() - if self.instance_jobs is not None: - result['InstanceJobs'] = self.instance_jobs.to_map() + result['ComponentVersions'] = [] + if self.component_versions is not None: + for k in self.component_versions: + result['ComponentVersions'].append(k.to_map() if k else None) if self.request_id is not None: result['RequestId'] = self.request_id if self.total_count is not None: @@ -16871,9 +15564,11 @@ def to_map(self): def from_map(self, m: dict = None): m = m or dict() - if m.get('InstanceJobs') is not None: - temp_model = ListInstanceJobsResponseBodyInstanceJobs() - self.instance_jobs = temp_model.from_map(m['InstanceJobs']) + self.component_versions = [] + if m.get('ComponentVersions') is not None: + for k in m.get('ComponentVersions'): + temp_model = ListComponentVersionsResponseBodyComponentVersions() + self.component_versions.append(temp_model.from_map(k)) if m.get('RequestId') is not None: self.request_id = m.get('RequestId') if m.get('TotalCount') is not None: @@ -16881,12 +15576,12 @@ def from_map(self, m: dict = None): return self -class ListInstanceJobsResponse(TeaModel): +class ListComponentVersionsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListInstanceJobsResponseBody = None, + body: ListComponentVersionsResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -16917,25 +15612,33 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListInstanceJobsResponseBody() + temp_model = ListComponentVersionsResponseBody() self.body = temp_model.from_map(m['body']) return self -class ListLLMProjectsRequest(TeaModel): +class ListComponentsRequest(TeaModel): def __init__( self, + component_id: str = None, + component_ids: str = None, + labels: Dict[str, Any] = None, + name: str = None, order: str = None, page_number: int = None, page_size: int = None, - project_name: str = None, + provider: str = None, sort_by: str = None, workspace_id: str = None, ): + self.component_id = component_id + self.component_ids = component_ids + self.labels = labels + self.name = name self.order = order self.page_number = page_number self.page_size = page_size - self.project_name = project_name + self.provider = provider self.sort_by = sort_by self.workspace_id = workspace_id @@ -16948,14 +15651,22 @@ def to_map(self): return _map result = dict() + if self.component_id is not None: + result['ComponentId'] = self.component_id + if self.component_ids is not None: + result['ComponentIds'] = self.component_ids + if self.labels is not None: + result['Labels'] = self.labels + if self.name is not None: + result['Name'] = self.name if self.order is not None: result['Order'] = self.order if self.page_number is not None: result['PageNumber'] = self.page_number if self.page_size is not None: result['PageSize'] = self.page_size - if self.project_name is not None: - result['ProjectName'] = self.project_name + if self.provider is not None: + result['Provider'] = self.provider if self.sort_by is not None: result['SortBy'] = self.sort_by if self.workspace_id is not None: @@ -16964,14 +15675,22 @@ def to_map(self): def from_map(self, m: dict = None): m = m or dict() + if m.get('ComponentId') is not None: + self.component_id = m.get('ComponentId') + if m.get('ComponentIds') is not None: + self.component_ids = m.get('ComponentIds') + if m.get('Labels') is not None: + self.labels = m.get('Labels') + if m.get('Name') is not None: + self.name = m.get('Name') if m.get('Order') is not None: self.order = m.get('Order') if m.get('PageNumber') is not None: self.page_number = m.get('PageNumber') if m.get('PageSize') is not None: self.page_size = m.get('PageSize') - if m.get('ProjectName') is not None: - self.project_name = m.get('ProjectName') + if m.get('Provider') is not None: + self.provider = m.get('Provider') if m.get('SortBy') is not None: self.sort_by = m.get('SortBy') if m.get('WorkspaceId') is not None: @@ -16979,14 +15698,30 @@ def from_map(self, m: dict = None): return self -class ListLLMProjectsResponseBodyProjectsLabels(TeaModel): +class ListComponentsShrinkRequest(TeaModel): def __init__( self, - key: str = None, - value: str = None, + component_id: str = None, + component_ids: str = None, + labels_shrink: str = None, + name: str = None, + order: str = None, + page_number: int = None, + page_size: int = None, + provider: str = None, + sort_by: str = None, + workspace_id: str = None, ): - self.key = key - self.value = value + self.component_id = component_id + self.component_ids = component_ids + self.labels_shrink = labels_shrink + self.name = name + self.order = order + self.page_number = page_number + self.page_size = page_size + self.provider = provider + self.sort_by = sort_by + self.workspace_id = workspace_id def validate(self): pass @@ -16997,29 +15732,66 @@ def to_map(self): return _map result = dict() - if self.key is not None: - result['Key'] = self.key - if self.value is not None: - result['Value'] = self.value + if self.component_id is not None: + result['ComponentId'] = self.component_id + if self.component_ids is not None: + result['ComponentIds'] = self.component_ids + if self.labels_shrink is not None: + result['Labels'] = self.labels_shrink + if self.name is not None: + result['Name'] = self.name + if self.order is not None: + result['Order'] = self.order + if self.page_number is not None: + result['PageNumber'] = self.page_number + if self.page_size is not None: + result['PageSize'] = self.page_size + if self.provider is not None: + result['Provider'] = self.provider + if self.sort_by is not None: + result['SortBy'] = self.sort_by + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Key') is not None: - self.key = m.get('Key') - if m.get('Value') is not None: - self.value = m.get('Value') + if m.get('ComponentId') is not None: + self.component_id = m.get('ComponentId') + if m.get('ComponentIds') is not None: + self.component_ids = m.get('ComponentIds') + if m.get('Labels') is not None: + self.labels_shrink = m.get('Labels') + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('Order') is not None: + self.order = m.get('Order') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + if m.get('Provider') is not None: + self.provider = m.get('Provider') + if m.get('SortBy') is not None: + self.sort_by = m.get('SortBy') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class ListLLMProjectsResponseBodyProjectsRuntime(TeaModel): +class ListComponentsResponseBodyComponentsVersions(TeaModel): def __init__( self, - runtime_id: str = None, - runtime_type: str = None, + gmt_create_time: str = None, + snapshot_id: str = None, + status: str = None, + version: str = None, ): - self.runtime_id = runtime_id - self.runtime_type = runtime_type + # Use the UTC time format: yyyy-MM-ddTHH:mmZ + self.gmt_create_time = gmt_create_time + self.snapshot_id = snapshot_id + self.status = status + self.version = version def validate(self): pass @@ -17030,48 +15802,58 @@ def to_map(self): return _map result = dict() - if self.runtime_id is not None: - result['RuntimeId'] = self.runtime_id - if self.runtime_type is not None: - result['RuntimeType'] = self.runtime_type + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.snapshot_id is not None: + result['SnapshotId'] = self.snapshot_id + if self.status is not None: + result['Status'] = self.status + if self.version is not None: + result['Version'] = self.version return result def from_map(self, m: dict = None): m = m or dict() - if m.get('RuntimeId') is not None: - self.runtime_id = m.get('RuntimeId') - if m.get('RuntimeType') is not None: - self.runtime_type = m.get('RuntimeType') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('SnapshotId') is not None: + self.snapshot_id = m.get('SnapshotId') + if m.get('Status') is not None: + self.status = m.get('Status') + if m.get('Version') is not None: + self.version = m.get('Version') return self -class ListLLMProjectsResponseBodyProjects(TeaModel): +class ListComponentsResponseBodyComponents(TeaModel): def __init__( self, + component_id: str = None, + description: str = None, + display_name: str = None, gmt_create_time: str = None, gmt_modified_time: str = None, - labels: List[ListLLMProjectsResponseBodyProjectsLabels] = None, - owner_id: str = None, - project_description: str = None, - project_id: str = None, - project_name: str = None, - project_type: str = None, - root_path: str = None, - runtime: ListLLMProjectsResponseBodyProjectsRuntime = None, + labels: List[Label] = None, + name: str = None, + provider: str = None, + tenant_id: str = None, user_id: str = None, + versions: List[ListComponentsResponseBodyComponentsVersions] = None, workspace_id: str = None, ): + self.component_id = component_id + self.description = description + self.display_name = display_name + # Use the UTC time format: yyyy-MM-ddTHH:mmZ self.gmt_create_time = gmt_create_time + # Use the UTC time format: yyyy-MM-ddTHH:mmZ self.gmt_modified_time = gmt_modified_time self.labels = labels - self.owner_id = owner_id - self.project_description = project_description - self.project_id = project_id - self.project_name = project_name - self.project_type = project_type - self.root_path = root_path - self.runtime = runtime + self.name = name + self.provider = provider + self.tenant_id = tenant_id self.user_id = user_id + self.versions = versions self.workspace_id = workspace_id def validate(self): @@ -17079,8 +15861,10 @@ def validate(self): for k in self.labels: if k: k.validate() - if self.runtime: - self.runtime.validate() + if self.versions: + for k in self.versions: + if k: + k.validate() def to_map(self): _map = super().to_map() @@ -17088,6 +15872,12 @@ def to_map(self): return _map result = dict() + if self.component_id is not None: + result['ComponentId'] = self.component_id + if self.description is not None: + result['Description'] = self.description + if self.display_name is not None: + result['DisplayName'] = self.display_name if self.gmt_create_time is not None: result['GmtCreateTime'] = self.gmt_create_time if self.gmt_modified_time is not None: @@ -17096,28 +15886,30 @@ def to_map(self): if self.labels is not None: for k in self.labels: result['Labels'].append(k.to_map() if k else None) - if self.owner_id is not None: - result['OwnerId'] = self.owner_id - if self.project_description is not None: - result['ProjectDescription'] = self.project_description - if self.project_id is not None: - result['ProjectId'] = self.project_id - if self.project_name is not None: - result['ProjectName'] = self.project_name - if self.project_type is not None: - result['ProjectType'] = self.project_type - if self.root_path is not None: - result['RootPath'] = self.root_path - if self.runtime is not None: - result['Runtime'] = self.runtime.to_map() + if self.name is not None: + result['Name'] = self.name + if self.provider is not None: + result['Provider'] = self.provider + if self.tenant_id is not None: + result['TenantId'] = self.tenant_id if self.user_id is not None: result['UserId'] = self.user_id + result['Versions'] = [] + if self.versions is not None: + for k in self.versions: + result['Versions'].append(k.to_map() if k else None) if self.workspace_id is not None: result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() + if m.get('ComponentId') is not None: + self.component_id = m.get('ComponentId') + if m.get('Description') is not None: + self.description = m.get('Description') + if m.get('DisplayName') is not None: + self.display_name = m.get('DisplayName') if m.get('GmtCreateTime') is not None: self.gmt_create_time = m.get('GmtCreateTime') if m.get('GmtModifiedTime') is not None: @@ -17125,42 +15917,40 @@ def from_map(self, m: dict = None): self.labels = [] if m.get('Labels') is not None: for k in m.get('Labels'): - temp_model = ListLLMProjectsResponseBodyProjectsLabels() + temp_model = Label() self.labels.append(temp_model.from_map(k)) - if m.get('OwnerId') is not None: - self.owner_id = m.get('OwnerId') - if m.get('ProjectDescription') is not None: - self.project_description = m.get('ProjectDescription') - if m.get('ProjectId') is not None: - self.project_id = m.get('ProjectId') - if m.get('ProjectName') is not None: - self.project_name = m.get('ProjectName') - if m.get('ProjectType') is not None: - self.project_type = m.get('ProjectType') - if m.get('RootPath') is not None: - self.root_path = m.get('RootPath') - if m.get('Runtime') is not None: - temp_model = ListLLMProjectsResponseBodyProjectsRuntime() - self.runtime = temp_model.from_map(m['Runtime']) + if m.get('Name') is not None: + self.name = m.get('Name') + if m.get('Provider') is not None: + self.provider = m.get('Provider') + if m.get('TenantId') is not None: + self.tenant_id = m.get('TenantId') if m.get('UserId') is not None: self.user_id = m.get('UserId') + self.versions = [] + if m.get('Versions') is not None: + for k in m.get('Versions'): + temp_model = ListComponentsResponseBodyComponentsVersions() + self.versions.append(temp_model.from_map(k)) if m.get('WorkspaceId') is not None: self.workspace_id = m.get('WorkspaceId') return self -class ListLLMProjectsResponseBody(TeaModel): +class ListComponentsResponseBody(TeaModel): def __init__( self, - projects: List[ListLLMProjectsResponseBodyProjects] = None, + components: List[ListComponentsResponseBodyComponents] = None, request_id: str = None, + total_count: int = None, ): - self.projects = projects + self.components = components self.request_id = request_id + self.total_count = total_count def validate(self): - if self.projects: - for k in self.projects: + if self.components: + for k in self.components: if k: k.validate() @@ -17170,32 +15960,36 @@ def to_map(self): return _map result = dict() - result['Projects'] = [] - if self.projects is not None: - for k in self.projects: - result['Projects'].append(k.to_map() if k else None) + result['Components'] = [] + if self.components is not None: + for k in self.components: + result['Components'].append(k.to_map() if k else None) if self.request_id is not None: result['RequestId'] = self.request_id + if self.total_count is not None: + result['TotalCount'] = self.total_count return result def from_map(self, m: dict = None): m = m or dict() - self.projects = [] - if m.get('Projects') is not None: - for k in m.get('Projects'): - temp_model = ListLLMProjectsResponseBodyProjects() - self.projects.append(temp_model.from_map(k)) + self.components = [] + if m.get('Components') is not None: + for k in m.get('Components'): + temp_model = ListComponentsResponseBodyComponents() + self.components.append(temp_model.from_map(k)) if m.get('RequestId') is not None: self.request_id = m.get('RequestId') + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') return self -class ListLLMProjectsResponse(TeaModel): +class ListComponentsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListLLMProjectsResponseBody = None, + body: ListComponentsResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -17226,23 +16020,27 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListLLMProjectsResponseBody() + temp_model = ListComponentsResponseBody() self.body = temp_model.from_map(m['body']) return self -class ListLLMSnapshotsRequest(TeaModel): +class ListInstanceJobsRequest(TeaModel): def __init__( self, + instance_job_type: str = None, order: str = None, page_number: int = None, page_size: int = None, sort_by: str = None, + status: str = None, ): + self.instance_job_type = instance_job_type self.order = order self.page_number = page_number self.page_size = page_size self.sort_by = sort_by + self.status = status def validate(self): pass @@ -17253,6 +16051,8 @@ def to_map(self): return _map result = dict() + if self.instance_job_type is not None: + result['InstanceJobType'] = self.instance_job_type if self.order is not None: result['Order'] = self.order if self.page_number is not None: @@ -17261,10 +16061,14 @@ def to_map(self): result['PageSize'] = self.page_size if self.sort_by is not None: result['SortBy'] = self.sort_by + if self.status is not None: + result['Status'] = self.status return result def from_map(self, m: dict = None): m = m or dict() + if m.get('InstanceJobType') is not None: + self.instance_job_type = m.get('InstanceJobType') if m.get('Order') is not None: self.order = m.get('Order') if m.get('PageNumber') is not None: @@ -17273,17 +16077,34 @@ def from_map(self, m: dict = None): self.page_size = m.get('PageSize') if m.get('SortBy') is not None: self.sort_by = m.get('SortBy') + if m.get('Status') is not None: + self.status = m.get('Status') return self -class ListLLMSnapshotsResponseBodySnapshotsContentStorage(TeaModel): +class ListInstanceJobsResponseBodyInstanceJobs(TeaModel): def __init__( self, - location: str = None, - type: str = None, + creator: str = None, + gmt_create_time: str = None, + instance_id: str = None, + instance_job_id: str = None, + instance_job_type: str = None, + reason_code: str = None, + reason_message: str = None, + status: str = None, + workspace_id: str = None, ): - self.location = location - self.type = type + self.creator = creator + # Use the UTC time format: yyyy-MM-ddTHH:mmZ + self.gmt_create_time = gmt_create_time + self.instance_id = instance_id + self.instance_job_id = instance_job_id + self.instance_job_type = instance_job_type + self.reason_code = reason_code + self.reason_message = reason_message + self.status = status + self.workspace_id = workspace_id def validate(self): pass @@ -17294,43 +16115,63 @@ def to_map(self): return _map result = dict() - if self.location is not None: - result['Location'] = self.location - if self.type is not None: - result['Type'] = self.type + if self.creator is not None: + result['Creator'] = self.creator + if self.gmt_create_time is not None: + result['GmtCreateTime'] = self.gmt_create_time + if self.instance_id is not None: + result['InstanceId'] = self.instance_id + if self.instance_job_id is not None: + result['InstanceJobId'] = self.instance_job_id + if self.instance_job_type is not None: + result['InstanceJobType'] = self.instance_job_type + if self.reason_code is not None: + result['ReasonCode'] = self.reason_code + if self.reason_message is not None: + result['ReasonMessage'] = self.reason_message + if self.status is not None: + result['Status'] = self.status + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Location') is not None: - self.location = m.get('Location') - if m.get('Type') is not None: - self.type = m.get('Type') + if m.get('Creator') is not None: + self.creator = m.get('Creator') + if m.get('GmtCreateTime') is not None: + self.gmt_create_time = m.get('GmtCreateTime') + if m.get('InstanceId') is not None: + self.instance_id = m.get('InstanceId') + if m.get('InstanceJobId') is not None: + self.instance_job_id = m.get('InstanceJobId') + if m.get('InstanceJobType') is not None: + self.instance_job_type = m.get('InstanceJobType') + if m.get('ReasonCode') is not None: + self.reason_code = m.get('ReasonCode') + if m.get('ReasonMessage') is not None: + self.reason_message = m.get('ReasonMessage') + if m.get('Status') is not None: + self.status = m.get('Status') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class ListLLMSnapshotsResponseBodySnapshots(TeaModel): +class ListInstanceJobsResponseBody(TeaModel): def __init__( self, - content_storage: ListLLMSnapshotsResponseBodySnapshotsContentStorage = None, - gmt_create_time: str = None, - gmt_modified_time: str = None, - owner_id: str = None, - project_id: str = None, - snapshot_id: str = None, - user_id: str = None, + instance_jobs: ListInstanceJobsResponseBodyInstanceJobs = None, + request_id: str = None, + total_count: int = None, ): - self.content_storage = content_storage - self.gmt_create_time = gmt_create_time - self.gmt_modified_time = gmt_modified_time - self.owner_id = owner_id - self.project_id = project_id - self.snapshot_id = snapshot_id - self.user_id = user_id + self.instance_jobs = instance_jobs + self.request_id = request_id + self.total_count = total_count def validate(self): - if self.content_storage: - self.content_storage.validate() + if self.instance_jobs: + self.instance_jobs.validate() def to_map(self): _map = super().to_map() @@ -17338,56 +16179,133 @@ def to_map(self): return _map result = dict() - if self.content_storage is not None: - result['ContentStorage'] = self.content_storage.to_map() - if self.gmt_create_time is not None: - result['GmtCreateTime'] = self.gmt_create_time - if self.gmt_modified_time is not None: - result['GmtModifiedTime'] = self.gmt_modified_time - if self.owner_id is not None: - result['OwnerId'] = self.owner_id - if self.project_id is not None: - result['ProjectId'] = self.project_id - if self.snapshot_id is not None: - result['SnapshotId'] = self.snapshot_id - if self.user_id is not None: - result['UserId'] = self.user_id + if self.instance_jobs is not None: + result['InstanceJobs'] = self.instance_jobs.to_map() + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.total_count is not None: + result['TotalCount'] = self.total_count return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ContentStorage') is not None: - temp_model = ListLLMSnapshotsResponseBodySnapshotsContentStorage() - self.content_storage = temp_model.from_map(m['ContentStorage']) - if m.get('GmtCreateTime') is not None: - self.gmt_create_time = m.get('GmtCreateTime') - if m.get('GmtModifiedTime') is not None: - self.gmt_modified_time = m.get('GmtModifiedTime') - if m.get('OwnerId') is not None: - self.owner_id = m.get('OwnerId') - if m.get('ProjectId') is not None: - self.project_id = m.get('ProjectId') - if m.get('SnapshotId') is not None: - self.snapshot_id = m.get('SnapshotId') - if m.get('UserId') is not None: - self.user_id = m.get('UserId') + if m.get('InstanceJobs') is not None: + temp_model = ListInstanceJobsResponseBodyInstanceJobs() + self.instance_jobs = temp_model.from_map(m['InstanceJobs']) + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') return self -class ListLLMSnapshotsResponseBody(TeaModel): +class ListInstanceJobsResponse(TeaModel): def __init__( self, - request_id: str = None, - snapshots: List[ListLLMSnapshotsResponseBodySnapshots] = None, - total_count: int = None, + headers: Dict[str, str] = None, + status_code: int = None, + body: ListInstanceJobsResponseBody = None, ): - self.request_id = request_id - self.snapshots = snapshots - self.total_count = total_count + self.headers = headers + self.status_code = status_code + self.body = body def validate(self): - if self.snapshots: - for k in self.snapshots: + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = ListInstanceJobsResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class ListNodeGPUMetricsRequest(TeaModel): + def __init__( + self, + end_time: str = None, + gputype: str = None, + metric_type: str = None, + node_type: str = None, + start_time: str = None, + ): + self.end_time = end_time + self.gputype = gputype + # This parameter is required. + self.metric_type = metric_type + self.node_type = node_type + self.start_time = start_time + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.end_time is not None: + result['EndTime'] = self.end_time + if self.gputype is not None: + result['GPUType'] = self.gputype + if self.metric_type is not None: + result['MetricType'] = self.metric_type + if self.node_type is not None: + result['NodeType'] = self.node_type + if self.start_time is not None: + result['StartTime'] = self.start_time + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('EndTime') is not None: + self.end_time = m.get('EndTime') + if m.get('GPUType') is not None: + self.gputype = m.get('GPUType') + if m.get('MetricType') is not None: + self.metric_type = m.get('MetricType') + if m.get('NodeType') is not None: + self.node_type = m.get('NodeType') + if m.get('StartTime') is not None: + self.start_time = m.get('StartTime') + return self + + +class ListNodeGPUMetricsResponseBody(TeaModel): + def __init__( + self, + metric_type: str = None, + node_gpumetrics: List[NodeGPUMetric] = None, + quota_id: str = None, + ): + self.metric_type = metric_type + self.node_gpumetrics = node_gpumetrics + self.quota_id = quota_id + + def validate(self): + if self.node_gpumetrics: + for k in self.node_gpumetrics: if k: k.validate() @@ -17397,36 +16315,36 @@ def to_map(self): return _map result = dict() - if self.request_id is not None: - result['RequestId'] = self.request_id - result['Snapshots'] = [] - if self.snapshots is not None: - for k in self.snapshots: - result['Snapshots'].append(k.to_map() if k else None) - if self.total_count is not None: - result['TotalCount'] = self.total_count + if self.metric_type is not None: + result['MetricType'] = self.metric_type + result['NodeGPUMetrics'] = [] + if self.node_gpumetrics is not None: + for k in self.node_gpumetrics: + result['NodeGPUMetrics'].append(k.to_map() if k else None) + if self.quota_id is not None: + result['QuotaId'] = self.quota_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - self.snapshots = [] - if m.get('Snapshots') is not None: - for k in m.get('Snapshots'): - temp_model = ListLLMSnapshotsResponseBodySnapshots() - self.snapshots.append(temp_model.from_map(k)) - if m.get('TotalCount') is not None: - self.total_count = m.get('TotalCount') + if m.get('MetricType') is not None: + self.metric_type = m.get('MetricType') + self.node_gpumetrics = [] + if m.get('NodeGPUMetrics') is not None: + for k in m.get('NodeGPUMetrics'): + temp_model = NodeGPUMetric() + self.node_gpumetrics.append(temp_model.from_map(k)) + if m.get('QuotaId') is not None: + self.quota_id = m.get('QuotaId') return self -class ListLLMSnapshotsResponse(TeaModel): +class ListNodeGPUMetricsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListLLMSnapshotsResponseBody = None, + body: ListNodeGPUMetricsResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -17457,7 +16375,7 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListLLMSnapshotsResponseBody() + temp_model = ListNodeGPUMetricsResponseBody() self.body = temp_model.from_map(m['body']) return self @@ -17467,6 +16385,7 @@ def __init__( self, resource_group_id: str = None, ): + # This parameter is required. self.resource_group_id = resource_group_id def validate(self): @@ -17723,7 +16642,10 @@ class ListNodesRequest(TeaModel): def __init__( self, accelerator_type: str = None, + filter_by_quota_id: str = None, + filter_by_resource_group_ids: str = None, gputype: str = None, + node_names: str = None, node_statuses: str = None, node_types: str = None, order: str = None, @@ -17736,7 +16658,10 @@ def __init__( verbose: bool = None, ): self.accelerator_type = accelerator_type + self.filter_by_quota_id = filter_by_quota_id + self.filter_by_resource_group_ids = filter_by_resource_group_ids self.gputype = gputype + self.node_names = node_names self.node_statuses = node_statuses self.node_types = node_types self.order = order @@ -17759,8 +16684,14 @@ def to_map(self): result = dict() if self.accelerator_type is not None: result['AcceleratorType'] = self.accelerator_type + if self.filter_by_quota_id is not None: + result['FilterByQuotaId'] = self.filter_by_quota_id + if self.filter_by_resource_group_ids is not None: + result['FilterByResourceGroupIds'] = self.filter_by_resource_group_ids if self.gputype is not None: result['GPUType'] = self.gputype + if self.node_names is not None: + result['NodeNames'] = self.node_names if self.node_statuses is not None: result['NodeStatuses'] = self.node_statuses if self.node_types is not None: @@ -17787,8 +16718,14 @@ def from_map(self, m: dict = None): m = m or dict() if m.get('AcceleratorType') is not None: self.accelerator_type = m.get('AcceleratorType') + if m.get('FilterByQuotaId') is not None: + self.filter_by_quota_id = m.get('FilterByQuotaId') + if m.get('FilterByResourceGroupIds') is not None: + self.filter_by_resource_group_ids = m.get('FilterByResourceGroupIds') if m.get('GPUType') is not None: self.gputype = m.get('GPUType') + if m.get('NodeNames') is not None: + self.node_names = m.get('NodeNames') if m.get('NodeStatuses') is not None: self.node_statuses = m.get('NodeStatuses') if m.get('NodeTypes') is not None: @@ -17812,20 +16749,267 @@ def from_map(self, m: dict = None): return self -class ListNodesResponseBody(TeaModel): +class ListNodesResponseBody(TeaModel): + def __init__( + self, + nodes: List[Node] = None, + request_id: str = None, + total_count: int = None, + ): + self.nodes = nodes + self.request_id = request_id + self.total_count = total_count + + def validate(self): + if self.nodes: + for k in self.nodes: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + result['Nodes'] = [] + if self.nodes is not None: + for k in self.nodes: + result['Nodes'].append(k.to_map() if k else None) + if self.request_id is not None: + result['RequestId'] = self.request_id + if self.total_count is not None: + result['TotalCount'] = self.total_count + return result + + def from_map(self, m: dict = None): + m = m or dict() + self.nodes = [] + if m.get('Nodes') is not None: + for k in m.get('Nodes'): + temp_model = Node() + self.nodes.append(temp_model.from_map(k)) + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') + return self + + +class ListNodesResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: ListNodesResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = ListNodesResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class ListOperationsRequest(TeaModel): + def __init__( + self, + object_id: str = None, + object_type: str = None, + operation_id: str = None, + operation_type: str = None, + order: str = None, + page_number: int = None, + page_size: int = None, + sort_by: str = None, + status: str = None, + ): + self.object_id = object_id + self.object_type = object_type + self.operation_id = operation_id + self.operation_type = operation_type + self.order = order + self.page_number = page_number + self.page_size = page_size + self.sort_by = sort_by + self.status = status + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.object_id is not None: + result['ObjectId'] = self.object_id + if self.object_type is not None: + result['ObjectType'] = self.object_type + if self.operation_id is not None: + result['OperationId'] = self.operation_id + if self.operation_type is not None: + result['OperationType'] = self.operation_type + if self.order is not None: + result['Order'] = self.order + if self.page_number is not None: + result['PageNumber'] = self.page_number + if self.page_size is not None: + result['PageSize'] = self.page_size + if self.sort_by is not None: + result['SortBy'] = self.sort_by + if self.status is not None: + result['Status'] = self.status + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('ObjectId') is not None: + self.object_id = m.get('ObjectId') + if m.get('ObjectType') is not None: + self.object_type = m.get('ObjectType') + if m.get('OperationId') is not None: + self.operation_id = m.get('OperationId') + if m.get('OperationType') is not None: + self.operation_type = m.get('OperationType') + if m.get('Order') is not None: + self.order = m.get('Order') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + if m.get('SortBy') is not None: + self.sort_by = m.get('SortBy') + if m.get('Status') is not None: + self.status = m.get('Status') + return self + + +class ListOperationsResponseBody(TeaModel): + def __init__( + self, + operations: List[ResourceOperation] = None, + request_id: str = None, + ): + self.operations = operations + self.request_id = request_id + + def validate(self): + if self.operations: + for k in self.operations: + if k: + k.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + result['Operations'] = [] + if self.operations is not None: + for k in self.operations: + result['Operations'].append(k.to_map() if k else None) + if self.request_id is not None: + result['RequestId'] = self.request_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + self.operations = [] + if m.get('Operations') is not None: + for k in m.get('Operations'): + temp_model = ResourceOperation() + self.operations.append(temp_model.from_map(k)) + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + return self + + +class ListOperationsResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: ListOperationsResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = ListOperationsResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + +class ListPermissionsResponseBody(TeaModel): def __init__( self, - nodes: List[Node] = None, + features: Features = None, + permissions: List[Permission] = None, request_id: str = None, - total_count: int = None, ): - self.nodes = nodes + self.features = features + self.permissions = permissions self.request_id = request_id - self.total_count = total_count def validate(self): - if self.nodes: - for k in self.nodes: + if self.features: + self.features.validate() + if self.permissions: + for k in self.permissions: if k: k.validate() @@ -17835,36 +17019,37 @@ def to_map(self): return _map result = dict() - result['Nodes'] = [] - if self.nodes is not None: - for k in self.nodes: - result['Nodes'].append(k.to_map() if k else None) + if self.features is not None: + result['Features'] = self.features.to_map() + result['Permissions'] = [] + if self.permissions is not None: + for k in self.permissions: + result['Permissions'].append(k.to_map() if k else None) if self.request_id is not None: - result['RequestId'] = self.request_id - if self.total_count is not None: - result['TotalCount'] = self.total_count + result['requestId'] = self.request_id return result def from_map(self, m: dict = None): m = m or dict() - self.nodes = [] - if m.get('Nodes') is not None: - for k in m.get('Nodes'): - temp_model = Node() - self.nodes.append(temp_model.from_map(k)) - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - if m.get('TotalCount') is not None: - self.total_count = m.get('TotalCount') + if m.get('Features') is not None: + temp_model = Features() + self.features = temp_model.from_map(m['Features']) + self.permissions = [] + if m.get('Permissions') is not None: + for k in m.get('Permissions'): + temp_model = Permission() + self.permissions.append(temp_model.from_map(k)) + if m.get('requestId') is not None: + self.request_id = m.get('requestId') return self -class ListNodesResponse(TeaModel): +class ListPermissionsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListNodesResponseBody = None, + body: ListPermissionsResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -17895,33 +17080,31 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListNodesResponseBody() + temp_model = ListPermissionsResponseBody() self.body = temp_model.from_map(m['body']) return self -class ListOperationsRequest(TeaModel): +class ListQuotaUsersRequest(TeaModel): def __init__( self, - object_id: str = None, - object_type: str = None, - operation_id: str = None, - operation_type: str = None, order: str = None, page_number: int = None, page_size: int = None, + self_only: bool = None, sort_by: str = None, - status: str = None, + user_id: str = None, + username: str = None, + workspace_id: str = None, ): - self.object_id = object_id - self.object_type = object_type - self.operation_id = operation_id - self.operation_type = operation_type self.order = order self.page_number = page_number self.page_size = page_size + self.self_only = self_only self.sort_by = sort_by - self.status = status + self.user_id = user_id + self.username = username + self.workspace_id = workspace_id def validate(self): pass @@ -17932,61 +17115,59 @@ def to_map(self): return _map result = dict() - if self.object_id is not None: - result['ObjectId'] = self.object_id - if self.object_type is not None: - result['ObjectType'] = self.object_type - if self.operation_id is not None: - result['OperationId'] = self.operation_id - if self.operation_type is not None: - result['OperationType'] = self.operation_type if self.order is not None: result['Order'] = self.order if self.page_number is not None: result['PageNumber'] = self.page_number if self.page_size is not None: result['PageSize'] = self.page_size + if self.self_only is not None: + result['SelfOnly'] = self.self_only if self.sort_by is not None: result['SortBy'] = self.sort_by - if self.status is not None: - result['Status'] = self.status + if self.user_id is not None: + result['UserId'] = self.user_id + if self.username is not None: + result['Username'] = self.username + if self.workspace_id is not None: + result['WorkspaceId'] = self.workspace_id return result def from_map(self, m: dict = None): m = m or dict() - if m.get('ObjectId') is not None: - self.object_id = m.get('ObjectId') - if m.get('ObjectType') is not None: - self.object_type = m.get('ObjectType') - if m.get('OperationId') is not None: - self.operation_id = m.get('OperationId') - if m.get('OperationType') is not None: - self.operation_type = m.get('OperationType') if m.get('Order') is not None: self.order = m.get('Order') if m.get('PageNumber') is not None: self.page_number = m.get('PageNumber') if m.get('PageSize') is not None: self.page_size = m.get('PageSize') + if m.get('SelfOnly') is not None: + self.self_only = m.get('SelfOnly') if m.get('SortBy') is not None: self.sort_by = m.get('SortBy') - if m.get('Status') is not None: - self.status = m.get('Status') + if m.get('UserId') is not None: + self.user_id = m.get('UserId') + if m.get('Username') is not None: + self.username = m.get('Username') + if m.get('WorkspaceId') is not None: + self.workspace_id = m.get('WorkspaceId') return self -class ListOperationsResponseBody(TeaModel): +class ListQuotaUsersResponseBody(TeaModel): def __init__( self, - operations: List[ResourceOperation] = None, + quota_users: List[QuotaUser] = None, request_id: str = None, + total_count: str = None, ): - self.operations = operations + self.quota_users = quota_users self.request_id = request_id + self.total_count = total_count def validate(self): - if self.operations: - for k in self.operations: + if self.quota_users: + for k in self.quota_users: if k: k.validate() @@ -17996,32 +17177,36 @@ def to_map(self): return _map result = dict() - result['Operations'] = [] - if self.operations is not None: - for k in self.operations: - result['Operations'].append(k.to_map() if k else None) + result['QuotaUsers'] = [] + if self.quota_users is not None: + for k in self.quota_users: + result['QuotaUsers'].append(k.to_map() if k else None) if self.request_id is not None: result['RequestId'] = self.request_id + if self.total_count is not None: + result['TotalCount'] = self.total_count return result def from_map(self, m: dict = None): m = m or dict() - self.operations = [] - if m.get('Operations') is not None: - for k in m.get('Operations'): - temp_model = ResourceOperation() - self.operations.append(temp_model.from_map(k)) + self.quota_users = [] + if m.get('QuotaUsers') is not None: + for k in m.get('QuotaUsers'): + temp_model = QuotaUser() + self.quota_users.append(temp_model.from_map(k)) if m.get('RequestId') is not None: self.request_id = m.get('RequestId') + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') return self -class ListOperationsResponse(TeaModel): +class ListQuotaUsersResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListOperationsResponseBody = None, + body: ListQuotaUsersResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -18052,27 +17237,132 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListOperationsResponseBody() + temp_model = ListQuotaUsersResponseBody() self.body = temp_model.from_map(m['body']) return self -class ListPermissionsResponseBody(TeaModel): +class ListQuotaWorkloadsRequest(TeaModel): + def __init__( + self, + before_workload_id: str = None, + node_name: str = None, + order: str = None, + page_number: int = None, + page_size: int = None, + show_own: bool = None, + sort_by: str = None, + status: str = None, + sub_quota_ids: str = None, + user_ids: str = None, + workload_created_time_range: TimeRangeFilter = None, + workload_ids: str = None, + workload_type: str = None, + workspace_ids: str = None, + ): + self.before_workload_id = before_workload_id + self.node_name = node_name + self.order = order + self.page_number = page_number + self.page_size = page_size + self.show_own = show_own + self.sort_by = sort_by + self.status = status + self.sub_quota_ids = sub_quota_ids + self.user_ids = user_ids + self.workload_created_time_range = workload_created_time_range + self.workload_ids = workload_ids + self.workload_type = workload_type + self.workspace_ids = workspace_ids + + def validate(self): + if self.workload_created_time_range: + self.workload_created_time_range.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.before_workload_id is not None: + result['BeforeWorkloadId'] = self.before_workload_id + if self.node_name is not None: + result['NodeName'] = self.node_name + if self.order is not None: + result['Order'] = self.order + if self.page_number is not None: + result['PageNumber'] = self.page_number + if self.page_size is not None: + result['PageSize'] = self.page_size + if self.show_own is not None: + result['ShowOwn'] = self.show_own + if self.sort_by is not None: + result['SortBy'] = self.sort_by + if self.status is not None: + result['Status'] = self.status + if self.sub_quota_ids is not None: + result['SubQuotaIds'] = self.sub_quota_ids + if self.user_ids is not None: + result['UserIds'] = self.user_ids + if self.workload_created_time_range is not None: + result['WorkloadCreatedTimeRange'] = self.workload_created_time_range.to_map() + if self.workload_ids is not None: + result['WorkloadIds'] = self.workload_ids + if self.workload_type is not None: + result['WorkloadType'] = self.workload_type + if self.workspace_ids is not None: + result['WorkspaceIds'] = self.workspace_ids + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('BeforeWorkloadId') is not None: + self.before_workload_id = m.get('BeforeWorkloadId') + if m.get('NodeName') is not None: + self.node_name = m.get('NodeName') + if m.get('Order') is not None: + self.order = m.get('Order') + if m.get('PageNumber') is not None: + self.page_number = m.get('PageNumber') + if m.get('PageSize') is not None: + self.page_size = m.get('PageSize') + if m.get('ShowOwn') is not None: + self.show_own = m.get('ShowOwn') + if m.get('SortBy') is not None: + self.sort_by = m.get('SortBy') + if m.get('Status') is not None: + self.status = m.get('Status') + if m.get('SubQuotaIds') is not None: + self.sub_quota_ids = m.get('SubQuotaIds') + if m.get('UserIds') is not None: + self.user_ids = m.get('UserIds') + if m.get('WorkloadCreatedTimeRange') is not None: + temp_model = TimeRangeFilter() + self.workload_created_time_range = temp_model.from_map(m['WorkloadCreatedTimeRange']) + if m.get('WorkloadIds') is not None: + self.workload_ids = m.get('WorkloadIds') + if m.get('WorkloadType') is not None: + self.workload_type = m.get('WorkloadType') + if m.get('WorkspaceIds') is not None: + self.workspace_ids = m.get('WorkspaceIds') + return self + + +class ListQuotaWorkloadsResponseBody(TeaModel): def __init__( self, - features: Features = None, - permissions: List[Permission] = None, request_id: str = None, + total_count: int = None, + workloads: List[QueueInfo] = None, ): - self.features = features - self.permissions = permissions self.request_id = request_id + self.total_count = total_count + self.workloads = workloads def validate(self): - if self.features: - self.features.validate() - if self.permissions: - for k in self.permissions: + if self.workloads: + for k in self.workloads: if k: k.validate() @@ -18081,38 +17371,37 @@ def to_map(self): if _map is not None: return _map - result = dict() - if self.features is not None: - result['Features'] = self.features.to_map() - result['Permissions'] = [] - if self.permissions is not None: - for k in self.permissions: - result['Permissions'].append(k.to_map() if k else None) + result = dict() if self.request_id is not None: - result['requestId'] = self.request_id + result['RequestId'] = self.request_id + if self.total_count is not None: + result['TotalCount'] = self.total_count + result['Workloads'] = [] + if self.workloads is not None: + for k in self.workloads: + result['Workloads'].append(k.to_map() if k else None) return result def from_map(self, m: dict = None): m = m or dict() - if m.get('Features') is not None: - temp_model = Features() - self.features = temp_model.from_map(m['Features']) - self.permissions = [] - if m.get('Permissions') is not None: - for k in m.get('Permissions'): - temp_model = Permission() - self.permissions.append(temp_model.from_map(k)) - if m.get('requestId') is not None: - self.request_id = m.get('requestId') + if m.get('RequestId') is not None: + self.request_id = m.get('RequestId') + if m.get('TotalCount') is not None: + self.total_count = m.get('TotalCount') + self.workloads = [] + if m.get('Workloads') is not None: + for k in m.get('Workloads'): + temp_model = QueueInfo() + self.workloads.append(temp_model.from_map(k)) return self -class ListPermissionsResponse(TeaModel): +class ListQuotaWorkloadsResponse(TeaModel): def __init__( self, headers: Dict[str, str] = None, status_code: int = None, - body: ListPermissionsResponseBody = None, + body: ListQuotaWorkloadsResponseBody = None, ): self.headers = headers self.status_code = status_code @@ -18143,7 +17432,7 @@ def from_map(self, m: dict = None): if m.get('statusCode') is not None: self.status_code = m.get('statusCode') if m.get('body') is not None: - temp_model = ListPermissionsResponseBody() + temp_model = ListQuotaWorkloadsResponseBody() self.body = temp_model.from_map(m['body']) return self @@ -18162,6 +17451,7 @@ def __init__( resource_type: str = None, sort_by: str = None, statuses: str = None, + verbose: bool = None, workspace_ids: str = None, ): self.labels = labels @@ -18175,6 +17465,7 @@ def __init__( self.resource_type = resource_type self.sort_by = sort_by self.statuses = statuses + self.verbose = verbose self.workspace_ids = workspace_ids def validate(self): @@ -18208,6 +17499,8 @@ def to_map(self): result['SortBy'] = self.sort_by if self.statuses is not None: result['Statuses'] = self.statuses + if self.verbose is not None: + result['Verbose'] = self.verbose if self.workspace_ids is not None: result['WorkspaceIds'] = self.workspace_ids return result @@ -18236,6 +17529,8 @@ def from_map(self, m: dict = None): self.sort_by = m.get('SortBy') if m.get('Statuses') is not None: self.statuses = m.get('Statuses') + if m.get('Verbose') is not None: + self.verbose = m.get('Verbose') if m.get('WorkspaceIds') is not None: self.workspace_ids = m.get('WorkspaceIds') return self @@ -18336,6 +17631,7 @@ def __init__( ecs_spec: str = None, name: str = None, order: str = None, + order_instance_id: str = None, page_number: int = None, page_size: int = None, payment_duration: str = None, @@ -18348,6 +17644,7 @@ def __init__( self.ecs_spec = ecs_spec self.name = name self.order = order + self.order_instance_id = order_instance_id self.page_number = page_number self.page_size = page_size self.payment_duration = payment_duration @@ -18373,6 +17670,8 @@ def to_map(self): result['Name'] = self.name if self.order is not None: result['Order'] = self.order + if self.order_instance_id is not None: + result['OrderInstanceId'] = self.order_instance_id if self.page_number is not None: result['PageNumber'] = self.page_number if self.page_size is not None: @@ -18399,6 +17698,8 @@ def from_map(self, m: dict = None): self.name = m.get('Name') if m.get('Order') is not None: self.order = m.get('Order') + if m.get('OrderInstanceId') is not None: + self.order_instance_id = m.get('OrderInstanceId') if m.get('PageNumber') is not None: self.page_number = m.get('PageNumber') if m.get('PageSize') is not None: @@ -18588,6 +17889,7 @@ def __init__( ): self.request_id = request_id self.resource_groups = resource_groups + # This parameter is required. self.total_count = total_count def validate(self): @@ -18819,8 +18121,10 @@ def __init__( tag: List[ListTagResourcesRequestTag] = None, ): self.next_token = next_token + # This parameter is required. self.region_id = region_id self.resource_id = resource_id + # This parameter is required. self.resource_type = resource_type self.tag = tag @@ -18878,8 +18182,10 @@ def __init__( tag_shrink: str = None, ): self.next_token = next_token + # This parameter is required. self.region_id = region_id self.resource_id_shrink = resource_id_shrink + # This parameter is required. self.resource_type = resource_type self.tag_shrink = tag_shrink @@ -19061,9 +18367,11 @@ def __init__( start_time: str = None, token: str = None, ): + # Use the UTC time format: yyyy-MM-ddTHH:mmZ self.end_time = end_time self.page_number = page_number self.page_size = page_size + # Use the UTC time format: yyyy-MM-ddTHH:mmZ self.start_time = start_time self.token = token @@ -19192,9 +18500,11 @@ def __init__( start_time: str = None, token: str = None, ): + # Use the UTC time format: yyyy-MM-ddTHH:mmZ self.end_time = end_time self.page_number = page_number self.page_size = page_size + # Use the UTC time format: yyyy-MM-ddTHH:mmZ self.start_time = start_time self.token = token @@ -19324,9 +18634,12 @@ def __init__( time_step: str = None, token: str = None, ): + # Use the UTC time format: yyyy-MM-ddTHH:mmZ self.end_time = end_time self.instance_id = instance_id + # This parameter is required. self.metric_type = metric_type + # Use the UTC time format: yyyy-MM-ddTHH:mmZ self.start_time = start_time self.time_step = time_step self.token = token @@ -19544,10 +18857,12 @@ def __init__( token: str = None, worker_id: str = None, ): + # Use the UTC time format: yyyy-MM-ddTHH:mmZ self.end_time = end_time self.instance_id = instance_id self.page_number = page_number self.page_size = page_size + # Use the UTC time format: yyyy-MM-ddTHH:mmZ self.start_time = start_time self.token = token self.worker_id = worker_id @@ -19687,11 +19002,13 @@ def __init__( start_time: str = None, token: str = None, ): + # Use the UTC time format: yyyy-MM-ddTHH:mmZ self.end_time = end_time self.name = name self.order = order self.page_number = page_number self.page_size = page_size + # Use the UTC time format: yyyy-MM-ddTHH:mmZ self.start_time = start_time self.token = token @@ -19747,6 +19064,7 @@ def __init__( value: float = None, ): self.name = name + # Use the UTC time format: yyyy-MM-ddTHH:mmZ self.timestamp = timestamp self.value = value @@ -21026,7 +20344,9 @@ def __init__( operation: str = None, resource_group_id: str = None, ): + # This parameter is required. self.operation = operation + # This parameter is required. self.resource_group_id = resource_group_id def validate(self): @@ -21133,6 +20453,7 @@ def __init__( target_algorithm_name: str = None, update_if_exists: bool = None, ): + # This parameter is required. self.target_algorithm_name = target_algorithm_name self.update_if_exists = update_if_exists @@ -21241,6 +20562,7 @@ def __init__( target_algorithm_version: str = None, update_if_exists: bool = None, ): + # This parameter is required. self.target_algorithm_name = target_algorithm_name self.target_algorithm_version = target_algorithm_version self.update_if_exists = update_if_exists @@ -21353,6 +20675,74 @@ def from_map(self, m: dict = None): return self +class ReleaseMachineGroupResponseBody(TeaModel): + def __init__( + self, + request_id: str = None, + ): + self.request_id = request_id + + def validate(self): + pass + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.request_id is not None: + result['requestId'] = self.request_id + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('requestId') is not None: + self.request_id = m.get('requestId') + return self + + +class ReleaseMachineGroupResponse(TeaModel): + def __init__( + self, + headers: Dict[str, str] = None, + status_code: int = None, + body: ReleaseMachineGroupResponseBody = None, + ): + self.headers = headers + self.status_code = status_code + self.body = body + + def validate(self): + if self.body: + self.body.validate() + + def to_map(self): + _map = super().to_map() + if _map is not None: + return _map + + result = dict() + if self.headers is not None: + result['headers'] = self.headers + if self.status_code is not None: + result['statusCode'] = self.status_code + if self.body is not None: + result['body'] = self.body.to_map() + return result + + def from_map(self, m: dict = None): + m = m or dict() + if m.get('headers') is not None: + self.headers = m.get('headers') + if m.get('statusCode') is not None: + self.status_code = m.get('statusCode') + if m.get('body') is not None: + temp_model = ReleaseMachineGroupResponseBody() + self.body = temp_model.from_map(m['body']) + return self + + class ScaleQuotaRequest(TeaModel): def __init__( self, @@ -21695,8 +21085,11 @@ def __init__( tag_key: List[str] = None, ): self.all = all + # This parameter is required. self.region_id = region_id + # This parameter is required. self.resource_id = resource_id + # This parameter is required. self.resource_type = resource_type self.tag_key = tag_key @@ -21746,8 +21139,11 @@ def __init__( tag_key_shrink: str = None, ): self.all = all + # This parameter is required. self.region_id = region_id + # This parameter is required. self.resource_id_shrink = resource_id_shrink + # This parameter is required. self.resource_type = resource_type self.tag_key_shrink = tag_key_shrink @@ -22390,224 +21786,28 @@ def from_map(self, m: dict = None): return self -class UpdateLLMProjectRequestLabels(TeaModel): - def __init__( - self, - key: str = None, - value: str = None, - ): - self.key = key - self.value = value - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.key is not None: - result['Key'] = self.key - if self.value is not None: - result['Value'] = self.value - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('Key') is not None: - self.key = m.get('Key') - if m.get('Value') is not None: - self.value = m.get('Value') - return self - - -class UpdateLLMProjectRequestRuntime(TeaModel): - def __init__( - self, - runtime_id: str = None, - runtime_type: str = None, - ): - self.runtime_id = runtime_id - self.runtime_type = runtime_type - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.runtime_id is not None: - result['RuntimeId'] = self.runtime_id - if self.runtime_type is not None: - result['RuntimeType'] = self.runtime_type - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('RuntimeId') is not None: - self.runtime_id = m.get('RuntimeId') - if m.get('RuntimeType') is not None: - self.runtime_type = m.get('RuntimeType') - return self - - -class UpdateLLMProjectRequest(TeaModel): - def __init__( - self, - labels: List[UpdateLLMProjectRequestLabels] = None, - project_description: str = None, - project_name: str = None, - root_path: str = None, - runtime: UpdateLLMProjectRequestRuntime = None, - ): - self.labels = labels - self.project_description = project_description - self.project_name = project_name - self.root_path = root_path - self.runtime = runtime - - def validate(self): - if self.labels: - for k in self.labels: - if k: - k.validate() - if self.runtime: - self.runtime.validate() - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - result['Labels'] = [] - if self.labels is not None: - for k in self.labels: - result['Labels'].append(k.to_map() if k else None) - if self.project_description is not None: - result['ProjectDescription'] = self.project_description - if self.project_name is not None: - result['ProjectName'] = self.project_name - if self.root_path is not None: - result['RootPath'] = self.root_path - if self.runtime is not None: - result['Runtime'] = self.runtime.to_map() - return result - - def from_map(self, m: dict = None): - m = m or dict() - self.labels = [] - if m.get('Labels') is not None: - for k in m.get('Labels'): - temp_model = UpdateLLMProjectRequestLabels() - self.labels.append(temp_model.from_map(k)) - if m.get('ProjectDescription') is not None: - self.project_description = m.get('ProjectDescription') - if m.get('ProjectName') is not None: - self.project_name = m.get('ProjectName') - if m.get('RootPath') is not None: - self.root_path = m.get('RootPath') - if m.get('Runtime') is not None: - temp_model = UpdateLLMProjectRequestRuntime() - self.runtime = temp_model.from_map(m['Runtime']) - return self - - -class UpdateLLMProjectResponseBody(TeaModel): - def __init__( - self, - project_id: str = None, - request_id: str = None, - ): - self.project_id = project_id - self.request_id = request_id - - def validate(self): - pass - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.project_id is not None: - result['ProjectId'] = self.project_id - if self.request_id is not None: - result['RequestId'] = self.request_id - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('ProjectId') is not None: - self.project_id = m.get('ProjectId') - if m.get('RequestId') is not None: - self.request_id = m.get('RequestId') - return self - - -class UpdateLLMProjectResponse(TeaModel): - def __init__( - self, - headers: Dict[str, str] = None, - status_code: int = None, - body: UpdateLLMProjectResponseBody = None, - ): - self.headers = headers - self.status_code = status_code - self.body = body - - def validate(self): - if self.body: - self.body.validate() - - def to_map(self): - _map = super().to_map() - if _map is not None: - return _map - - result = dict() - if self.headers is not None: - result['headers'] = self.headers - if self.status_code is not None: - result['statusCode'] = self.status_code - if self.body is not None: - result['body'] = self.body.to_map() - return result - - def from_map(self, m: dict = None): - m = m or dict() - if m.get('headers') is not None: - self.headers = m.get('headers') - if m.get('statusCode') is not None: - self.status_code = m.get('statusCode') - if m.get('body') is not None: - temp_model = UpdateLLMProjectResponseBody() - self.body = temp_model.from_map(m['body']) - return self - - class UpdateQuotaRequest(TeaModel): def __init__( self, description: str = None, labels: List[Label] = None, queue_strategy: str = None, + quota_config: QuotaConfig = None, + quota_name: str = None, ): self.description = description self.labels = labels self.queue_strategy = queue_strategy + self.quota_config = quota_config + self.quota_name = quota_name def validate(self): if self.labels: for k in self.labels: if k: k.validate() + if self.quota_config: + self.quota_config.validate() def to_map(self): _map = super().to_map() @@ -22623,6 +21823,10 @@ def to_map(self): result['Labels'].append(k.to_map() if k else None) if self.queue_strategy is not None: result['QueueStrategy'] = self.queue_strategy + if self.quota_config is not None: + result['QuotaConfig'] = self.quota_config.to_map() + if self.quota_name is not None: + result['QuotaName'] = self.quota_name return result def from_map(self, m: dict = None): @@ -22636,6 +21840,11 @@ def from_map(self, m: dict = None): self.labels.append(temp_model.from_map(k)) if m.get('QueueStrategy') is not None: self.queue_strategy = m.get('QueueStrategy') + if m.get('QuotaConfig') is not None: + temp_model = QuotaConfig() + self.quota_config = temp_model.from_map(m['QuotaConfig']) + if m.get('QuotaName') is not None: + self.quota_name = m.get('QuotaName') return self diff --git a/pai/model/_model_recipe.py b/pai/model/_model_recipe.py index 39f35fc..9d982f8 100644 --- a/pai/model/_model_recipe.py +++ b/pai/model/_model_recipe.py @@ -29,6 +29,8 @@ InstanceSpec, ModelRecipeSpec, OssLocation, + ResourceType, + SpotSpec, TrainingJob, UriInput, UserVpcConfig, @@ -51,6 +53,7 @@ class RecipeInitKwargs(object): model_channel_name: Optional[str] model_uri: Optional[str] hyperparameters: Optional[Dict[str, Any]] + # hyperparameter_definitions: Optional[List[HyperParameterDefinition]] job_type: Optional[str] image_uri: Optional[str] source_dir: Optional[str] @@ -66,6 +69,8 @@ class RecipeInitKwargs(object): input_channels: Optional[List[Channel]] output_channels: Optional[List[Channel]] default_inputs: Optional[Union[UriInput, DatasetConfig]] + customization: Optional[Dict[str, Any]] + supported_instance_types: Optional[List[str]] class ModelRecipeType(enum.Enum): @@ -99,6 +104,8 @@ def __init__( instance_type: Optional[str] = None, instance_spec: Optional[InstanceSpec] = None, resource_id: Optional[str] = None, + resource_type: Optional[Union[str, ResourceType]] = None, + spot_spec: Optional[SpotSpec] = None, user_vpc_config: Optional[UserVpcConfig] = None, labels: Optional[Dict[str, str]] = None, requirements: Optional[List[str]] = None, @@ -109,6 +116,8 @@ def __init__( max_run_time: Optional[int] = None, default_inputs: Optional[Dict[str, Any]] = None, base_job_name: Optional[str] = None, + supported_instance_type: Optional[List[str]] = None, + settings: Optional[Dict[str, Any]] = None, ): init_kwargs = self._init_kwargs( model_name=model_name, @@ -134,6 +143,7 @@ def __init__( output_channels=output_channels, default_inputs=default_inputs, max_run_time=max_run_time, + supported_instance_types=supported_instance_type, ) self.model_name = init_kwargs.model_name self.model_version = init_kwargs.model_version @@ -147,12 +157,18 @@ def __init__( self.command = init_kwargs.command self.source_dir = init_kwargs.source_dir self.default_inputs = init_kwargs.default_inputs + self.customization = init_kwargs.customization + self.supported_instance_types = init_kwargs.supported_instance_types + self.input_channels = init_kwargs.input_channels + self.output_channels = init_kwargs.output_channels super().__init__( + resource_type=resource_type, base_job_name=base_job_name, experiment_config=experiment_config, resource_id=resource_id, user_vpc_config=user_vpc_config, + spot_spec=spot_spec, instance_type=init_kwargs.instance_type, instance_count=init_kwargs.instance_count, instance_spec=init_kwargs.instance_spec, @@ -160,6 +176,7 @@ def __init__( environments=init_kwargs.environments, requirements=init_kwargs.requirements, labels=init_kwargs.labels, + settings=settings, ) @classmethod @@ -188,6 +205,7 @@ def _init_kwargs( input_channels: List[Channel] = None, output_channels: List[Channel] = None, default_inputs: Optional[Union[UriInput, DatasetConfig]] = None, + supported_instance_types: Optional[List[str]] = None, ) -> RecipeInitKwargs: model = ( RegisteredModel( @@ -204,6 +222,7 @@ def _init_kwargs( else None ) model_uri = model_uri or (model and model.uri) + customization = None if not model_recipe_spec: return RecipeInitKwargs( model_name=model_name, @@ -228,6 +247,8 @@ def _init_kwargs( output_channels=output_channels, max_run_time=max_run_time, default_inputs=default_inputs, + customization=customization, + supported_instance_types=supported_instance_types, ) if not model_uri: input_ = next( @@ -257,22 +278,31 @@ def _init_kwargs( else: default_inputs[item.name] = item algorithm_spec = cls._get_algorithm_spec(model_recipe_spec) + supported_instance_types = ( + supported_instance_types or model_recipe_spec.supported_instance_types + ) if algorithm_spec: if ( not source_dir and algorithm_spec.code_dir - and isinstance(algorithm_spec.code_dir, OssLocation) + and isinstance(algorithm_spec.code_dir.location_value, OssLocation) ): - source_dir = f"oss://{0}.{1}/{2}".format( - algorithm_spec.code_dir.bucket, - algorithm_spec.code_dir.endpoint, - algorithm_spec.code_dir.key.lstrip("/"), - ) + oss_location = algorithm_spec.code_dir.location_value + if oss_location.endpoint: + source_dir = f"oss://{oss_location.bucket}.{oss_location.endpoint}/{oss_location.key.lstrip('/')}" + else: + source_dir = ( + f"oss://{oss_location.bucket}/{oss_location.key.lstrip('/')}" + ) image_uri = image_uri or algorithm_spec.image command = command or algorithm_spec.command job_type = job_type or algorithm_spec.job_type input_channels = input_channels or algorithm_spec.input_channels output_channels = output_channels or algorithm_spec.output_channels + customization = algorithm_spec.customization + supported_instance_types = ( + supported_instance_types or algorithm_spec.supported_channel_types + ) instance_type, instance_spec, instance_count = cls._get_compute_resource_config( instance_type=instance_type, @@ -280,6 +310,7 @@ def _init_kwargs( instance_count=instance_count, resource_id=resource_id, compute_resource=model_recipe_spec.compute_resource, + supported_instance_types=supported_instance_types, ) hyperparameters = hyperparameters or {} hyperparameters = { @@ -295,6 +326,7 @@ def _init_kwargs( } requirements = requirements or model_recipe_spec.requirements environments = environments or model_recipe_spec.environments + return RecipeInitKwargs( model_name=model_name, model_version=model_version, @@ -318,6 +350,8 @@ def _init_kwargs( output_channels=output_channels, resource_id=resource_id, default_inputs=default_inputs, + customization=customization, + supported_instance_types=supported_instance_types, ) @staticmethod @@ -327,6 +361,7 @@ def _get_compute_resource_config( instance_spec: InstanceSpec, resource_id: str, compute_resource: ComputeResource, + supported_instance_types: List[str], ) -> Tuple[str, InstanceSpec, int]: if resource_id: if instance_type: @@ -355,7 +390,12 @@ def _get_compute_resource_config( compute_resource and compute_resource.ecs_spec ) if not instance_type: - raise ValueError("No instance type is specified for the training job") + if not supported_instance_types: + raise ValueError( + "No instance type is specified for the training job" + ) + else: + instance_type = supported_instance_types[0] instance_count = ( instance_count or (compute_resource and compute_resource.ecs_count) or 1 ) @@ -395,11 +435,14 @@ def _build_algorithm_spec( image=self.image_uri, job_type=self.job_type, code_dir=code_input, - output_channels=self._default_training_output_channels(), - input_channels=[ + output_channels=self.output_channels + or self._default_training_output_channels(), + input_channels=self.input_channels + or [ Channel(name=channel_name, required=False) for channel_name in inputs.keys() ], + customization=self.customization, ) return algorithm_spec @@ -514,8 +557,10 @@ def __init__( command: Union[str, List[str]] = None, instance_count: Optional[int] = None, instance_type: Optional[str] = None, + spot_spec: Optional[SpotSpec] = None, instance_spec: Optional[InstanceSpec] = None, resource_id: Optional[str] = None, + resource_type: Optional[Union[str, ResourceType]] = None, user_vpc_config: Optional[UserVpcConfig] = None, labels: Optional[Dict[str, str]] = None, requirements: Optional[List[str]] = None, @@ -526,6 +571,7 @@ def __init__( max_run_time: Optional[int] = None, default_training_inputs: Optional[Dict[str, Any]] = None, base_job_name: Optional[str] = None, + **kwargs, ): """Initialize a ModelTrainingRecipe object. @@ -567,6 +613,11 @@ def __init__( be provided when the instance spec is set. Default to None. resource_id (str, optional): The ID of the resource group used to run the training job. Default to None. + spot_spec (:class:`pai.model.SpotSpec`, optional): The spot instance config + used to run the training job. If provided, spot instance will be used. + resource_type (str, optional): The resource type used to run the training job. + By default, general computing resource is used. If the resource_type is + 'Lingjun', Lingjun computing resource is used. user_vpc_config (:class:`pai.model.UserVpcConfig`, optional): The VPC configuration used to enable the job instance to connect to the specified user VPC. Default to None. @@ -594,7 +645,9 @@ def __init__( instance_count=instance_count, instance_type=instance_type, instance_spec=instance_spec, + resource_type=resource_type, resource_id=resource_id, + spot_spec=spot_spec, user_vpc_config=user_vpc_config, labels=labels, requirements=requirements, @@ -605,6 +658,7 @@ def __init__( max_run_time=max_run_time, default_inputs=default_training_inputs, base_job_name=base_job_name, + **kwargs, ) def train( diff --git a/pai/processor.py b/pai/processor.py index e73b085..6626c20 100644 --- a/pai/processor.py +++ b/pai/processor.py @@ -23,10 +23,13 @@ Channel, CodeDir, ExperimentConfig, + SpotSpec, TrainingJob, + UriOutput, UserVpcConfig, _TrainingJobSubmitter, ) +from .job._training_job import ResourceType from .session import Session, get_default_session logger = get_logger(__name__) @@ -47,9 +50,12 @@ def __init__( base_job_name: Optional[str] = None, output_path: Optional[str] = None, instance_type: Optional[str] = None, + spot_spec: Optional[SpotSpec] = None, + resource_type: Optional[Union[str, ResourceType]] = None, instance_count: Optional[int] = None, user_vpc_config: Optional[UserVpcConfig] = None, experiment_config: Optional[ExperimentConfig] = None, + settings: Optional[Dict[str, Any]] = None, labels: Optional[Dict[str, str]] = None, session: Optional[Session] = None, ): @@ -142,6 +148,9 @@ def __init__( If the instance_type is "local", the job is executed locally using docker. instance_count (int): The number of machines used to run the job. + resource_type (str, optional): The resource type used to run the training job. + By default, general computing resource is used. If the resource_type is + 'Lingjun', Lingjun computing resource is used. user_vpc_config (:class:`pai.estimator.UserVpcConfig`, optional): The VPC configuration used to enable the job instance to connect to the specified user VPC. If provided, an Elastic Network Interface (ENI) will @@ -152,6 +161,8 @@ def __init__( experiment configuration used to construct the relationship between the job and the experiment. If provided, the training job will belong to the specified experiment, in which case the job will use artifact_uri of + settings (dict, optional): A dictionary that represents the additional settings + for job, such as AIMaster configurations. experiment as default output path. Default to None. labels (Dict[str, str], optional): A dictionary that maps label names to their values. This optional field allows you to provide a set of labels @@ -170,6 +181,8 @@ def __init__( self._input_channels = None self._output_channels = None super().__init__( + resource_type=resource_type, + spot_spec=spot_spec, base_job_name=base_job_name, output_path=output_path, experiment_config=experiment_config, @@ -180,6 +193,7 @@ def __init__( environments=environments, requirements=requirements, labels=labels, + settings=settings, ) def run( @@ -307,8 +321,8 @@ def get_outputs_data(self) -> Dict[str, str]: raise RuntimeError("Current no Job for the processor.") return { - ch["Name"]: ch["OutputUri"] or ch["DatasetId"] - for ch in self.latest_job.output_channels + ch.name: ch.output_uri if isinstance(ch, UriOutput) else ch.dataset_id + for ch in self.latest_job.outputs } def set_input_channels(self, channels: List[Channel]): diff --git a/pai/version.py b/pai/version.py index 32f748d..65ac819 100644 --- a/pai/version.py +++ b/pai/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -VERSION = "0.4.8" +VERSION = "0.4.9.dev0" diff --git a/tests/integration/test_estimator.py b/tests/integration/test_estimator.py index 52e266b..d91bbf4 100644 --- a/tests/integration/test_estimator.py +++ b/tests/integration/test_estimator.py @@ -15,7 +15,7 @@ import os import posixpath import re -from unittest import skipUnless +from unittest import skipIf, skipUnless import pytest @@ -24,7 +24,7 @@ from pai.estimator import AlgorithmEstimator, Estimator from pai.experiment import Experiment from pai.image import retrieve -from pai.job._training_job import ExperimentConfig +from pai.job._training_job import ExperimentConfig, ResourceType, SpotSpec from pai.session import get_default_session from tests.integration import BaseIntegTestCase from tests.integration.utils import t_context @@ -72,11 +72,24 @@ def test_xgb_train(self): "test": self.breast_cancer_test_data_uri, }, ) - model_path = os.path.join(os.path.join(est.model_data(), "model.json")) - self.assertTrue(self.is_oss_object_exists(model_path)) + @skipIf(t_context.support_spot_instance, "Skip spot instance test") + def test_use_spot_instance(self): + xgb_image_uri = retrieve("xgboost", framework_version="latest").image_uri + est = Estimator( + command="echo helloworld", + instance_type="ml.gu7ef.8xlarge-gu100", + image_uri=xgb_image_uri, + spot_spec=SpotSpec( + spot_strategy="SpotWithPriceLimit", + spot_discount_limit=0.5, + ), + resource_type=ResourceType.Lingjun, + ) + est.fit() + def test_torch_run(self): torch_image_uri = retrieve("pytorch", framework_version="1.12").image_uri est = Estimator( diff --git a/tests/integration/test_model/test_model_recipe.py b/tests/integration/test_model/test_model_recipe.py index 55d4e6c..954c779 100644 --- a/tests/integration/test_model/test_model_recipe.py +++ b/tests/integration/test_model/test_model_recipe.py @@ -12,12 +12,16 @@ # See the License for the specific language governing permissions and # limitations under the License. import os +from unittest import skipIf import pytest from pai.common.utils import camel_to_snake, random_str -from pai.model import RegisteredModel +from pai.job import SpotSpec +from pai.job._training_job import ResourceType +from pai.model import ModelTrainingRecipe, RegisteredModel from tests.integration import BaseIntegTestCase +from tests.integration.utils import t_context from tests.test_data import test_data_dir @@ -61,6 +65,26 @@ def test_training_e2e(self): ) self.assertIsNotNone(resp.choices[0].message.content) + @skipIf(t_context.support_spot_instance, "Skip spot instance test") + def test_spot_instance(self): + training_recipe = ModelTrainingRecipe( + model_name="qwen2-7b-instruct", + model_provider="pai", + method="Standard", + resource_type=ResourceType.Lingjun, + spot_spec=SpotSpec( + spot_strategy="SpotWithPriceLimit", + spot_discount_limit=0.5, + ), + instance_type="ml.gu7ef.8xlarge-gu100", + ) + train_data = os.path.join(test_data_dir, "chinese_medical/train_sampled.json") + training_recipe.train( + inputs={ + "train": train_data, + }, + ) + def test_custom_inputs_train(self): model = RegisteredModel(model_name="qwen1.5-0.5b-chat", model_provider="pai") training_recipe = model.training_recipe(method="QLoRA_LLM") diff --git a/tests/integration/utils.py b/tests/integration/utils.py index bf6a3c0..7d4cd41 100644 --- a/tests/integration/utils.py +++ b/tests/integration/utils.py @@ -103,6 +103,10 @@ def has_gpu(self): def is_inner(self): return self.pai_service_config.region_id == "center" + @property + def support_spot_instance(self): + return self.pai_service_config.region_id == "cn-wulanchabu" + @classmethod def _load_test_config(cls): test_config = os.environ.get("PAI_TEST_CONFIG", "test.ini") From ca7322c8399aa885b884b04ffbb40c6d49d4728f Mon Sep 17 00:00:00 2001 From: LiangQuan Date: Fri, 12 Jul 2024 09:25:08 +0800 Subject: [PATCH 36/59] feat: Storage/SharedMemorry configuration supports in EAS service (#34) * feat: eas storage config supports * fix: fix `has_docker` check pending * fix: fix AlgorithmEstimator init * add RawStorageConfig used for eas storage configuration * fix test case run for spot_instance * fix ModelRecipe.run not return job instance * add test case for custom args in model recipe * add test case for RawStorageConfig --- pai/estimator.py | 10 +- pai/huggingface/model.py | 7 + pai/job/_training_job.py | 1 + pai/model/__init__.py | 12 ++ pai/model/_model.py | 124 +++++++++++++++++- pai/model/_model_recipe.py | 9 +- pai/modelscope/model.py | 7 + pai/processor.py | 4 +- tests/integration/test_estimator.py | 4 +- tests/integration/test_model/test_model.py | 8 ++ .../test_model/test_model_recipe.py | 38 +++++- tests/integration/utils.py | 4 +- tests/unit/test_inference_spec.py | 66 +++++++++- 13 files changed, 278 insertions(+), 16 deletions(-) diff --git a/pai/estimator.py b/pai/estimator.py index 58a539d..8a8d936 100644 --- a/pai/estimator.py +++ b/pai/estimator.py @@ -1164,9 +1164,9 @@ def _get_hyperparameters( if hps_def: # Get default hyperparameters. for hp in hps_def: - hp_name = hp.get("Name") - hp_value = hp.get("DefaultValue", "") - hp_type = hp.get("Type", "String") + hp_name = hp.name + hp_value = hp.default_value + hp_type = hp.type or "String" # For hyperparameters with type INT or FLOAT, if the default value is # empty, skip it. if ( @@ -1232,8 +1232,8 @@ def fit( `/ml/input/data/{channel_name}` directory in the training container. wait (bool): Specifies whether to block until the training job is completed, either succeeded, failed, or stopped. (Default True). - show_logs (bool): Specifies whether to show the logs produced by the - training job (Default True). + show_logs (bool): Whether to show the logs of the training job. Default to True. + Note that the logs will be shown only when the `wait` is set to True. job_name (str, optional): The name of the training job. Returns: diff --git a/pai/huggingface/model.py b/pai/huggingface/model.py index 455dd26..cc15f57 100644 --- a/pai/huggingface/model.py +++ b/pai/huggingface/model.py @@ -21,6 +21,7 @@ DefaultServiceConfig, ModelBase, ResourceConfig, + StorageConfigBase, container_serving_spec, ) from ..serializers import SerializerBase @@ -76,6 +77,7 @@ def __init__( requirements: Optional[List[str]] = None, requirements_path: Optional[str] = None, health_check: Optional[Dict[str, Any]] = None, + storage_configs: Optional[List[StorageConfigBase]] = None, session: Optional[Session] = None, ): """Initialize a HuggingFace Model. @@ -144,6 +146,9 @@ def __init__( health_check (Dict[str, Any], optional): The health check configuration. If it not set, A TCP readiness probe will be used to check the health of the Model server. + storage_configs (List[StorageConfigBase], optional): A list of storage configs + used to mount the storage to the container. The storage can be OSS, NFS, + SharedMemory, or NodeStorage, etc. session (:class:`pai.session.Session`, optional): A pai session object manages interactions with PAI REST API. @@ -170,6 +175,7 @@ def __init__( self.requirements = requirements self.requirements_path = requirements_path self.health_check = health_check + self.storage_configs = storage_configs super(HuggingFaceModel, self).__init__( model_data=self.model_data, @@ -342,6 +348,7 @@ def deploy( requirements=self.requirements, requirements_path=self.requirements_path, health_check=self.health_check, + storage_configs=self.storage_configs, session=self.session, ) diff --git a/pai/job/_training_job.py b/pai/job/_training_job.py index 6af5e60..10db9bd 100644 --- a/pai/job/_training_job.py +++ b/pai/job/_training_job.py @@ -813,6 +813,7 @@ def _submit( ) if wait: training_job.wait(show_logs=show_logs) + return training_job @classmethod def _get_input_config( diff --git a/pai/model/__init__.py b/pai/model/__init__.py index 7f52e57..62e551e 100644 --- a/pai/model/__init__.py +++ b/pai/model/__init__.py @@ -17,8 +17,14 @@ InferenceSpec, Model, ModelFormat, + NfsStorageConfig, + NodeStorageConfig, + OssStorageConfig, + RawStorageConfig, RegisteredModel, ResourceConfig, + SharedMemoryConfig, + StorageConfigBase, container_serving_spec, ) from ._model_recipe import ModelRecipe, ModelRecipeType, ModelTrainingRecipe @@ -33,4 +39,10 @@ "ModelTrainingRecipe", "ModelRecipe", "ModelRecipeType", + "StorageConfigBase", + "NfsStorageConfig", + "NodeStorageConfig", + "SharedMemoryConfig", + "OssStorageConfig", + "RawStorageConfig", ] diff --git a/pai/model/_model.py b/pai/model/_model.py index 43ab038..b95966e 100644 --- a/pai/model/_model.py +++ b/pai/model/_model.py @@ -24,6 +24,7 @@ import time import typing import warnings +from abc import ABCMeta, abstractmethod from typing import Any, Dict, Iterator, List, Optional, Tuple, Union import requests @@ -74,6 +75,120 @@ class DefaultServiceConfig(object): code_path = "/ml/usercode/" +class StorageConfigBase(metaclass=ABCMeta): + """Base Storage Configuration.""" + + @abstractmethod + def to_dict(self): + pass + + +class RawStorageConfig(StorageConfigBase): + def __init__(self, config: Dict[str, Any]): + self.config = config + + def to_dict(self): + return self.config + + +class OssStorageConfig(StorageConfigBase): + """Configuration for OSS Storage.""" + + def __init__( + self, mount_path: str, oss_path: str, oss_endpoint: Optional[str] = None + ) -> None: + """ + Args: + mount_path (str): The target path where the OSS storage will be mounted. + oss_path (str): The source OSS path, must start with `oss://`. e.g. `oss://bucket-name/path/to/data`. + oss_endpoint (Optional[str]): The endpoint address of the OSS bucket, if not provided, + the internal endpoint for the bucket will be used. + """ + self.mount_path = mount_path + self.oss_path = oss_path + self.oss_endpoint = oss_endpoint + + def to_dict(self) -> Dict[str, Any]: + d = { + "mount_path": self.mount_path, + "oss": {"path": self.oss_path}, + } + + if self.oss_endpoint: + d["oss"]["endpoint"] = self.oss_endpoint + return d + + +class NfsStorageConfig(StorageConfigBase): + """Configuration for NFS Storage.""" + + def __init__( + self, + mount_path: str, + nfs_server: str, + nfs_path: str = "/", + read_only: bool = False, + ) -> None: + """ + Args: + mount_path (str): The target path where the NFS storage will be mounted. + nfs_server (str): The NFS server address. e.g. `xxx.cn-shanghai.nas.aliyuncs.com' + nfs_path (str): The source path in the NFS storage, default to '/'. + read_only (bool): Indicates if the NFS storage should be mounted as read-only, default to False. + """ + self.mount_path = mount_path + self.nfs_path = nfs_path + self.read_only = read_only + self.nfs_server = nfs_server + + def to_dict(self) -> Dict[str, Any]: + return { + "mount_path": self.mount_path, + "nfs": { + "path": self.nfs_path, + "readOnly": self.read_only, + "server": self.nfs_server, + }, + } + + +class NodeStorageConfig(StorageConfigBase): + """Use to mount the local node disk storage to the container.""" + + def __init__(self, mount_path) -> None: + """ + Args: + mount_path (str): The target path where the node disk storage will be mounted. + """ + self.mount_path = mount_path + + def to_dict(self) -> Dict[str, Any]: + return { + "empty_dir": {}, + "mount_path": self.mount_path, + } + + +class SharedMemoryConfig(StorageConfigBase): + """Use to configure the shared memory for the container.""" + + def __init__(self, size_limit: int) -> None: + """ + Args: + size_limit (int): Size limit of the shared memory, in GB. + """ + self.size_limit = size_limit + + def to_dict(self) -> Dict[str, Any]: + return { + "empty_dir": { + "medium": "memory", + "size_limit": self.size_limit, + }, + "mount_path": "/dev/shm", + } + + class ResourceConfig(object): """A class that represents the resource used by a PAI prediction service instance.""" @@ -465,6 +580,7 @@ def container_serving_spec( requirements: Optional[List[str]] = None, requirements_path: Optional[str] = None, health_check: Optional[Dict[str, Any]] = None, + storage_configs: Optional[List[StorageConfigBase]] = None, session: Optional[Session] = None, ) -> InferenceSpec: """A convenient function to create an InferenceSpec instance that serving the model @@ -539,6 +655,9 @@ def container_serving_spec( health_check (Dict[str, Any], optional): The health check configuration. If it not set, A TCP readiness probe will be used to check the health of the HTTP server. + storage_configs (List[StorageConfigBase], optional): A list of storage configs + used to mount the storage to the container. The storage can be OSS, NFS, + SharedMemory, or NodeStorage, etc. session (Session, optional): A PAI session instance used for communicating with PAI service. @@ -619,9 +738,12 @@ def container_serving_spec( container_spec["prepare"] = { "pythonRequirementsPath": requirements_path, } - inference_spec = InferenceSpec(containers=[container_spec]) + if storage_configs: + storage = [s.to_dict() for s in storage_configs] + inference_spec.storage = storage + # mount the uploaded serving scripts to the serving container. if source_dir: inference_spec.mount( diff --git a/pai/model/_model_recipe.py b/pai/model/_model_recipe.py index 9d982f8..62327bc 100644 --- a/pai/model/_model_recipe.py +++ b/pai/model/_model_recipe.py @@ -26,6 +26,7 @@ ComputeResource, DatasetConfig, ExperimentConfig, + HyperParameterDefinition, InstanceSpec, ModelRecipeSpec, OssLocation, @@ -53,7 +54,7 @@ class RecipeInitKwargs(object): model_channel_name: Optional[str] model_uri: Optional[str] hyperparameters: Optional[Dict[str, Any]] - # hyperparameter_definitions: Optional[List[HyperParameterDefinition]] + hyperparameter_definitions: Optional[List[HyperParameterDefinition]] job_type: Optional[str] image_uri: Optional[str] source_dir: Optional[str] @@ -161,6 +162,7 @@ def __init__( self.supported_instance_types = init_kwargs.supported_instance_types self.input_channels = init_kwargs.input_channels self.output_channels = init_kwargs.output_channels + self.hyperparameter_definitions = init_kwargs.hyperparameter_definitions super().__init__( resource_type=resource_type, @@ -249,6 +251,7 @@ def _init_kwargs( default_inputs=default_inputs, customization=customization, supported_instance_types=supported_instance_types, + hyperparameter_definitions=None, ) if not model_uri: input_ = next( @@ -281,6 +284,7 @@ def _init_kwargs( supported_instance_types = ( supported_instance_types or model_recipe_spec.supported_instance_types ) + hyperparameter_definitions = None if algorithm_spec: if ( not source_dir @@ -303,6 +307,7 @@ def _init_kwargs( supported_instance_types = ( supported_instance_types or algorithm_spec.supported_channel_types ) + hyperparameter_definitions = algorithm_spec.hyperparameter_definitions instance_type, instance_spec, instance_count = cls._get_compute_resource_config( instance_type=instance_type, @@ -352,6 +357,7 @@ def _init_kwargs( default_inputs=default_inputs, customization=customization, supported_instance_types=supported_instance_types, + hyperparameter_definitions=hyperparameter_definitions, ) @staticmethod @@ -684,6 +690,7 @@ def train( job_name (str, optional): The name of the training job. If not provided, a default job name will be generated. show_logs (bool): Whether to show the logs of the training job. Default to True. + Note that the logs will be shown only when the `wait` is set to True. Returns: :class:`pai.training.TrainingJob`: A submitted training job. diff --git a/pai/modelscope/model.py b/pai/modelscope/model.py index 971bdb7..246b31b 100644 --- a/pai/modelscope/model.py +++ b/pai/modelscope/model.py @@ -21,6 +21,7 @@ DefaultServiceConfig, ModelBase, ResourceConfig, + StorageConfigBase, container_serving_spec, ) from ..serializers import SerializerBase @@ -76,6 +77,7 @@ def __init__( requirements: Optional[List[str]] = None, requirements_path: Optional[str] = None, health_check: Optional[Dict[str, Any]] = None, + storage_configs: Optional[List[StorageConfigBase]] = None, session: Optional[Session] = None, ): """Initialize a ModelScope Model. @@ -144,6 +146,9 @@ def __init__( health_check (Dict[str, Any], optional): The health check configuration. If it not set, A TCP readiness probe will be used to check the health of the Model server. + storage_configs (List[StorageConfigBase], optional): A list of storage configs + used to mount the storage to the container. The storage can be OSS, NFS, + SharedMemory, or NodeStorage, etc. session (:class:`pai.session.Session`, optional): A pai session object manages interactions with PAI REST API. @@ -168,6 +173,7 @@ def __init__( self.requirements = requirements self.requirements_path = requirements_path self.health_check = health_check + self.storage_configs = storage_configs super(ModelScopeModel, self).__init__( model_data=self.model_data, session=session, @@ -340,6 +346,7 @@ def deploy( requirements_path=self.requirements_path, health_check=self.health_check, session=self.session, + storage_configs=self.storage_configs, ) return super(ModelScopeModel, self).deploy( service_name=service_name, diff --git a/pai/processor.py b/pai/processor.py index 6626c20..1d4a524 100644 --- a/pai/processor.py +++ b/pai/processor.py @@ -218,8 +218,8 @@ def run( `/ml/outputs/data/{channel_name}` directory in the job container. wait (bool): Specifies whether to block until the training job is completed, either succeeded, failed, or stopped. (Default True). - show_logs (bool): Specifies whether to show the logs produced by the - job (Default True). + show_logs (bool): Whether to show the logs of the job. Default to True. + Note that the logs will be shown only when the `wait` is set to True. Returns: :class:`pai.job.TrainingJob`: A submitted training job. diff --git a/tests/integration/test_estimator.py b/tests/integration/test_estimator.py index d91bbf4..7efe140 100644 --- a/tests/integration/test_estimator.py +++ b/tests/integration/test_estimator.py @@ -15,7 +15,7 @@ import os import posixpath import re -from unittest import skipIf, skipUnless +from unittest import skipUnless import pytest @@ -75,7 +75,7 @@ def test_xgb_train(self): model_path = os.path.join(os.path.join(est.model_data(), "model.json")) self.assertTrue(self.is_oss_object_exists(model_path)) - @skipIf(t_context.support_spot_instance, "Skip spot instance test") + @skipUnless(t_context.support_spot_instance, "Skip spot instance test") def test_use_spot_instance(self): xgb_image_uri = retrieve("xgboost", framework_version="latest").image_uri est = Estimator( diff --git a/tests/integration/test_model/test_model.py b/tests/integration/test_model/test_model.py index 1d24ca0..d6e3a5d 100644 --- a/tests/integration/test_model/test_model.py +++ b/tests/integration/test_model/test_model.py @@ -29,8 +29,10 @@ from pai.model import ( InferenceSpec, Model, + NodeStorageConfig, RegisteredModel, ResourceConfig, + SharedMemoryConfig, container_serving_spec, ) from tests.integration import BaseIntegTestCase @@ -79,11 +81,17 @@ def test_container_serving(self): command="python serving.py", image_uri=image_uri, port=5000, + storage_configs=[ + SharedMemoryConfig(size_limit=1), + NodeStorageConfig(mount_path="/ml/disk/"), + ], ) + self.assertEqual(len(inference_spec.storage), 3) model = Model( inference_spec=inference_spec, model_data=os.path.join(test_data_dir, "xgb_model/model.json"), ) + predictor = model.deploy( service_name=make_eas_service_name("container_serving"), instance_type="ecs.c6.xlarge", diff --git a/tests/integration/test_model/test_model_recipe.py b/tests/integration/test_model/test_model_recipe.py index 954c779..c8a83b1 100644 --- a/tests/integration/test_model/test_model_recipe.py +++ b/tests/integration/test_model/test_model_recipe.py @@ -12,11 +12,13 @@ # See the License for the specific language governing permissions and # limitations under the License. import os -from unittest import skipIf +from pathlib import Path +from unittest import skipUnless import pytest from pai.common.utils import camel_to_snake, random_str +from pai.image import retrieve from pai.job import SpotSpec from pai.job._training_job import ResourceType from pai.model import ModelTrainingRecipe, RegisteredModel @@ -65,7 +67,7 @@ def test_training_e2e(self): ) self.assertIsNotNone(resp.choices[0].message.content) - @skipIf(t_context.support_spot_instance, "Skip spot instance test") + @skipUnless(t_context.support_spot_instance, "Skip spot instance test") def test_spot_instance(self): training_recipe = ModelTrainingRecipe( model_name="qwen2-7b-instruct", @@ -93,12 +95,14 @@ def test_custom_inputs_train(self): "Default inputs is empty for ModelTrainingRecipe.", ) + self.assertIsNotNone(training_recipe.hyperparameter_definitions) train_data = os.path.join(test_data_dir, "chinese_medical/train_sampled.json") - training_recipe.train( + training_job = training_recipe.train( inputs={ "train": train_data, }, ) + self.assertIsNotNone(training_job) self.assertIsNotNone(training_recipe.model_data()) predictor = training_recipe.deploy( service_name=self._gen_service_name("test_custom"), @@ -113,3 +117,31 @@ def test_custom_inputs_train(self): max_tokens=100, ) self.assertIsNotNone(resp.choices[0].message.content) + + def test_custom_args(self): + command = ["echo", "helloworld"] + xgb_img = retrieve("xgboost", "latest") + hps = { + "num_train_epochs": "helloworld", + } + session = self.default_session + + recipe = ModelTrainingRecipe( + model_name="qwen1.5-0.5b-chat", + model_provider="pai", + source_dir=str(Path(test_data_dir) / "xgb_train"), + command=command, + hyperparameters=hps, + image_uri=xgb_img.image_uri, + ) + job = recipe.train( + wait=False, + ) + self.assertListEqual(job.algorithm_spec.command, command) + self.assertEqual(job.algorithm_spec.image, xgb_img.image_uri) + job_hps = {hp.name: hp.value for hp in job.hyperparameters if hp.name in hps} + self.assertDictEqual(job_hps, hps) + self.assertEqual( + job.algorithm_spec.code_dir.location_value.bucket, + session.oss_bucket.bucket_name, + ) diff --git a/tests/integration/utils.py b/tests/integration/utils.py index 7d4cd41..86be75b 100644 --- a/tests/integration/utils.py +++ b/tests/integration/utils.py @@ -90,7 +90,9 @@ def has_docker(self): return ( shutil.which("docker") is not None and subprocess.run( - ["docker", "stats"], stdout=subprocess.PIPE, stderr=subprocess.PIPE + ["docker", "stats", "--no-stream"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, ).returncode == 0 ) diff --git a/tests/unit/test_inference_spec.py b/tests/unit/test_inference_spec.py index 3d175cc..6e254b7 100644 --- a/tests/unit/test_inference_spec.py +++ b/tests/unit/test_inference_spec.py @@ -13,7 +13,15 @@ # limitations under the License. from pai.exception import DuplicatedMountException -from pai.model import InferenceSpec, container_serving_spec +from pai.model import ( + InferenceSpec, + NfsStorageConfig, + NodeStorageConfig, + OssStorageConfig, + RawStorageConfig, + SharedMemoryConfig, + container_serving_spec, +) from tests.unit import BaseUnitTestCase @@ -110,3 +118,59 @@ def test_set_model(self): model_path_v2 = "oss://pai-sdk-example/path/to/model/v2/" infer_spec.set_model_data(model_path_v2) self.assertEqual(model_path_v2, infer_spec.storage[1].oss.path) + + def test_storage(self): + infer_spec = container_serving_spec( + command="python3 /ml/code/model.py", + image_uri="python:3", + storage_configs=[ + OssStorageConfig( + mount_path="/ml/model/", + oss_path="oss://pai-sdk-example/path/to/model/", + ), + NfsStorageConfig( + mount_path="/ml/shared/", + nfs_server="nfs://abc", + nfs_path="/path/to/shared/", + ), + SharedMemoryConfig(size_limit=64), + NodeStorageConfig(mount_path="/ml/disk/"), + RawStorageConfig( + config={ + "image": { + "image": "MyImageUri", + "path": "/path/to/mount/", + }, + "mount_path": "/data_image", + } + ), + ], + ) + + truth = [ + { + "mount_path": "/ml/model/", + "oss": {"path": "oss://pai-sdk-example/path/to/model/"}, + }, + { + "mount_path": "/ml/shared/", + "nfs": { + "path": "/path/to/shared/", + "readOnly": False, + "server": "nfs://abc", + }, + }, + { + "empty_dir": {"medium": "memory", "size_limit": 64}, + "mount_path": "/dev/shm", + }, + {"empty_dir": {}, "mount_path": "/ml/disk/"}, + { + "image": { + "image": "MyImageUri", + "path": "/path/to/mount/", + }, + "mount_path": "/data_image", + }, + ] + self.assertListEqual(truth, infer_spec.storage) From 3c3b1628ea4f22836c4734c853e86f79973d60d2 Mon Sep 17 00:00:00 2001 From: LiangQuan Date: Fri, 12 Jul 2024 13:30:48 +0800 Subject: [PATCH 37/59] release: 0.4.9 (#35) --- pai/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pai/version.py b/pai/version.py index 65ac819..02e162a 100644 --- a/pai/version.py +++ b/pai/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -VERSION = "0.4.9.dev0" +VERSION = "0.4.9" From cbe8ac76046e8ba3c9e86bd8dc18eec5b72368b0 Mon Sep 17 00:00:00 2001 From: "luoyiyun.lyy" Date: Mon, 15 Jul 2024 16:30:05 +0800 Subject: [PATCH 38/59] feat: Add support for spot instance in Lingjun environment --- pai/api/training_job.py | 2 +- pai/job/_training_job.py | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/pai/api/training_job.py b/pai/api/training_job.py index 25697c8..4ff1f86 100644 --- a/pai/api/training_job.py +++ b/pai/api/training_job.py @@ -139,7 +139,7 @@ def create( compute_resource = CreateTrainingJobRequestComputeResource( ecs_count=instance_count, ecs_spec=instance_type, - use_spot_instance=bool(spot_spec), + # use_spot_instance=bool(spot_spec), spot_spec=spot_spec, ) elif instance_spec: diff --git a/pai/job/_training_job.py b/pai/job/_training_job.py index 10db9bd..82fe0a4 100644 --- a/pai/job/_training_job.py +++ b/pai/job/_training_job.py @@ -779,6 +779,16 @@ def _submit( spot_spec["SpotDiscountLimit"] = self.spot_spec.spot_discount_limit else: spot_spec = None + + # user vpc + if self.user_vpc_config: + user_vpc_config = { + "VpcId": self.user_vpc_config.vpc_id, + "SecurityGroupId": self.user_vpc_config.security_group_id, + } + else: + user_vpc_config = None + training_job_id = session.training_job_api.create( instance_count=instance_count, instance_spec=instance_spec.model_dump() if instance_spec else None, From 9386329b525ef92025ce7650224d8a2e2c24ae01 Mon Sep 17 00:00:00 2001 From: LiangQuan Date: Thu, 18 Jul 2024 09:20:41 +0800 Subject: [PATCH 39/59] build: support both `pai` and `alipai` package release (#37) * build: support both `pai` and `alipai` package release * switch readthedocs and package name to `pai` --- .github/workflows/publish.yaml | 20 ++++++++++++++++---- .github/workflows/release_trigger.yaml | 18 ++++++++++++++---- README.md | 4 ++-- README_EN.md | 4 ++-- docs/source/quick-tour/installation.rst | 2 +- setup.py | 3 ++- 6 files changed, 37 insertions(+), 14 deletions(-) diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 0a0cd40..1b1f902 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -14,7 +14,8 @@ jobs: runs-on: ubuntu-latest env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} + PAI_PYPI_TOKEN: ${{ secrets.PAI_PYPI_TOKEN }} + ALIPAI_PYPI_TOKEN: ${{ secrets.ALIPAI_PYPI_TOKEN }} steps: - uses: actions/checkout@v4 - name: Set up Python 3.8 @@ -23,7 +24,18 @@ jobs: python-version: '3.8' - name: Install dependencies run: pip install wheel setuptools twine - - name: Build package + # build and upload package pai + - name: Build package for pai run: python setup.py sdist bdist_wheel - - name: Publish package to PyPI - run: twine upload dist/* --skip-existing -u __token__ -p $PYPI_TOKEN + - name: Publish package to PyPI (pai) + run: twine upload dist/* --skip-existing -u __token__ -p $PAI_PYPI_TOKEN + - name: cleanup + run: | + rm -rf dist + rm -rf build + rm -rf pai.egg-info + # build and upload package alipai + - name: Build package for alipai + run: PACKAGE_NAME=alipai python setup.py sdist bdist_wheel + - name: Publish package to PyPI (alipai) + run: twine upload dist/* --skip-existing -u __token__ -p $ALIPAI_PYPI_TOKEN diff --git a/.github/workflows/release_trigger.yaml b/.github/workflows/release_trigger.yaml index cf45247..2d6ee91 100644 --- a/.github/workflows/release_trigger.yaml +++ b/.github/workflows/release_trigger.yaml @@ -17,8 +17,9 @@ jobs: runs-on: ubuntu-latest if: github.event.pull_request.merged == true && startsWith(github.head_ref, 'releases/v') env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} + PAI_PYPI_TOKEN: ${{ secrets.PAI_PYPI_TOKEN }} + ALIPAI_PYPI_TOKEN: ${{ secrets.ALIPAI_PYPI_TOKEN }} steps: - uses: actions/checkout@v4 - name: Set up Python 3.8 @@ -43,7 +44,16 @@ jobs: # git tag pushed by GitHub action bot will not trigger another action. - name: Install dependencies run: pip install wheel setuptools twine - - name: Build package + - name: Build package for pai run: python setup.py sdist bdist_wheel - - name: Publish package to PyPI - run: twine upload dist/* --skip-existing -u __token__ -p $PYPI_TOKEN + - name: Publish package to PyPI (pai) + run: twine upload dist/* --skip-existing -u __token__ -p $PAI_PYPI_TOKEN + - name: cleanup + run: | + rm -rf dist + rm -rf build + rm -rf pai.egg-info + - name: Build package for alipai + run: PACKAGE_NAME=alipai python setup.py sdist bdist_wheel + - name: Publish package to PyPI (alipai) + run: twine upload dist/* --skip-existing -u __token__ -p $ALIPAI_PYPI_TOKEN diff --git a/README.md b/README.md index 57cfbb6..5306161 100644 --- a/README.md +++ b/README.md @@ -9,12 +9,12 @@ PAI Python SDK是阿里云 [机器学习平台 PAI(Platform for Artificial Intel 使用以下命令安装PAI Python SDK(支持Python版本 \>= 3.8): ```shell -python -m pip install alipai +python -m pip install pai ``` ## 📖 文档 -请通过访问 [PAI Python SDK文档](https://alipai.readthedocs.io/) 或是查看 [docs](./docs) 目录下的文件获取SDK的详细文档,包括用户指南和API文档。 +请通过访问 [PAI Python SDK文档](https://pai.readthedocs.io/) 或是查看 [docs](./docs) 目录下的文件获取SDK的详细文档,包括用户指南和API文档。 ## 🛠 使用示例 diff --git a/README_EN.md b/README_EN.md index 851b9f0..833f99d 100644 --- a/README_EN.md +++ b/README_EN.md @@ -10,12 +10,12 @@ The PAI Python SDK is provided by Alibaba Cloud\'s [Platform for Artificial Inte Install the PAI Python SDK using the following command, which supports Python versions \>= 3.8 : ```shell -python -m pip install alipai +python -m pip install pai ``` ## 📖 Documentation -Find detailed documentation, including API references and user guides, in the [docs](./docs/) directory or visit [PAI Python SDK Documentation](https://alipai.readthedocs.io/). +Find detailed documentation, including API references and user guides, in the [docs](./docs/) directory or visit [PAI Python SDK Documentation](https://pai.readthedocs.io/). ## 🛠 Basic Usage diff --git a/docs/source/quick-tour/installation.rst b/docs/source/quick-tour/installation.rst index 181353f..b160c7a 100644 --- a/docs/source/quick-tour/installation.rst +++ b/docs/source/quick-tour/installation.rst @@ -9,7 +9,7 @@ .. parsed-literal:: - python -m pip install alipai + python -m pip install pai 前提条件 diff --git a/setup.py b/setup.py index 1dad53c..8df16cc 100644 --- a/setup.py +++ b/setup.py @@ -7,6 +7,7 @@ pkg_root = os.path.dirname(os.path.abspath(__file__)) REQUIREMENTS_FILE = "requirements/requirements.txt" +PACKAGE_NAME = os.getenv("PACKAGE_NAME", "pai") version_data = {} with open(os.path.join(pkg_root, "pai/version.py")) as fp: @@ -25,7 +26,7 @@ def read_requirements(): long_description = f.read() setup( - name="alipai", + name=PACKAGE_NAME, python_requires=">=3.8", version=version, setup_requires=["setuptools_scm"], From 46b3200d432760a3ae55f20d1628669d08cc5352 Mon Sep 17 00:00:00 2001 From: LiangQuan Date: Thu, 18 Jul 2024 09:26:06 +0800 Subject: [PATCH 40/59] release: 0.4.9.post0 (#38) --- pai/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pai/version.py b/pai/version.py index 02e162a..7764bf3 100644 --- a/pai/version.py +++ b/pai/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -VERSION = "0.4.9" +VERSION = "0.4.9.post0" From bf4dba2ad2e2fa46d85d57026fb62f01cf806db8 Mon Sep 17 00:00:00 2001 From: LiangQuan Date: Thu, 22 Aug 2024 13:00:01 +0800 Subject: [PATCH 41/59] feat: setup default session in DSW environment (#40) * fix: correct vpc endpoint used for network detection * feat: setup default session in DSW * fix network config * fix endpoint of pai-eas vpc network * bump to dev version --- pai/api/api_container.py | 2 +- pai/api/client_factory.py | 7 +- pai/common/consts.py | 2 +- pai/session.py | 148 ++++++++++++++++++++++++++++++++++-- pai/toolkit/config.py | 5 +- pai/toolkit/helper/utils.py | 38 ++++----- pai/version.py | 2 +- 7 files changed, 169 insertions(+), 35 deletions(-) diff --git a/pai/api/api_container.py b/pai/api/api_container.py index 3faace8..ba24d58 100644 --- a/pai/api/api_container.py +++ b/pai/api/api_container.py @@ -82,7 +82,7 @@ def __init__( else: self.network = ( Network.VPC - if is_domain_connectable(PAI_VPC_ENDPOINT) + if is_domain_connectable(PAI_VPC_ENDPOINT.format(self._region_id)) else Network.PUBLIC ) diff --git a/pai/api/client_factory.py b/pai/api/client_factory.py index 07d3dd1..0f0552a 100644 --- a/pai/api/client_factory.py +++ b/pai/api/client_factory.py @@ -87,7 +87,12 @@ def get_endpoint( raise ValueError("Please provide region_id to get the endpoint.") if network and network != Network.PUBLIC: - subdomain = f"{service_name}-{network.value.lower()}" + if service_name == "pai-eas": + # see endpoint list provided by PAI-EAS + # https://next.api.aliyun.com/product/eas + subdomain = f"pai-eas-manage-{network.value.lower()}" + else: + subdomain = f"{service_name}-{network.value.lower()}" else: subdomain = service_name return DEFAULT_SERVICE_ENDPOINT_PATTERN.format(subdomain, region_id) diff --git a/pai/common/consts.py b/pai/common/consts.py index b2cce95..c57c70c 100644 --- a/pai/common/consts.py +++ b/pai/common/consts.py @@ -23,7 +23,7 @@ DEFAULT_NETWORK_TYPE = os.environ.get("PAI_NETWORK_TYPE", None) # PAI VPC endpoint -PAI_VPC_ENDPOINT = "pai-vpc.aliyuncs.com" +PAI_VPC_ENDPOINT = "pai-vpc.{}.aliyuncs.com" class Network(enum.Enum): diff --git a/pai/session.py b/pai/session.py index 7db682d..ae84987 100644 --- a/pai/session.py +++ b/pai/session.py @@ -18,14 +18,20 @@ import os.path import posixpath from datetime import datetime -from typing import Any, Dict, Optional, Union +from typing import Any, Dict, Optional, Tuple, Union import oss2 +from alibabacloud_credentials.client import Client as CredentialClient +from alibabacloud_credentials.exceptions import CredentialException from alibabacloud_credentials.models import Config as CredentialConfig from alibabacloud_credentials.utils import auth_constant +from Tea.exceptions import TeaException from .api.api_container import ResourceAPIsContainerMixin -from .common.consts import DEFAULT_CONFIG_PATH, Network +from .api.base import ServiceName +from .api.client_factory import ClientFactory +from .api.workspace import WorkspaceAPI, WorkspaceConfigKeys +from .common.consts import DEFAULT_CONFIG_PATH, PAI_VPC_ENDPOINT, Network from .common.logging import get_logger from .common.oss_utils import CredentialProviderWrapper, OssUriObj from .common.utils import is_domain_connectable, make_list_resource_iterator @@ -150,12 +156,81 @@ def get_default_session() -> "Session": global _default_session if not _default_session: config = load_default_config_file() - if not config: - return - _default_session = Session(**config) + if config: + _default_session = Session(**config) + else: + _default_session = _init_default_session_from_env() return _default_session +def _init_default_session_from_env() -> Optional["Session"]: + credential_client = Session._get_default_credential_client() + if not credential_client: + logger.debug("Not found credential from default credential provider chain.") + return + + # legacy region id env var in DSW + region_id = os.getenv("dsw_region") + region_id = os.getenv("REGION", region_id) + if not region_id: + logger.debug( + "No region id found(env var: REGION or dsw_region), skip init default session" + ) + return + + dsw_instance_id = os.getenv("DSW_INSTANCE_ID") + if not dsw_instance_id: + logger.debug( + "No dsw instance id (env var: DSW_INSTANCE_ID) found, skip init default session" + ) + return + + workspace_id = os.getenv("PAI_AI_WORKSPACE_ID") + workspace_id = os.getenv("PAI_WORKSPACE_ID", workspace_id) + + network = ( + Network.VPC + if is_domain_connectable( + PAI_VPC_ENDPOINT.format(region_id), + timeout=1, + ) + else Network.PUBLIC + ) + + if dsw_instance_id and not workspace_id: + logger.debug("Getting workspace id by dsw instance id: %s", dsw_instance_id) + workspace_id = Session._get_workspace_id_by_dsw_instance_id( + dsw_instance_id=dsw_instance_id, + cred=credential_client, + region_id=region_id, + network=network, + ) + if not workspace_id: + logger.warning( + "Failed to get workspace id by dsw instance id: %s", dsw_instance_id + ) + return + bucket_name, oss_endpoint = Session.get_default_oss_storage( + workspace_id, credential_client, region_id, network + ) + + if not bucket_name: + logger.warning( + "Default OSS storage is not configured for the workspace: %s", workspace_id + ) + + sess = Session( + region_id=region_id, + workspace_id=workspace_id, + credential_config=None, + oss_bucket_name=bucket_name, + oss_endpoint=oss_endpoint, + network=network, + ) + + return sess + + def load_default_config_file() -> Optional[Dict[str, Any]]: """Read config file""" @@ -451,3 +526,66 @@ def is_gpu_inference_instance(self, instance_type: str) -> bool: "Please provide a supported instance type." ) return bool(spec["GPU"]) + + @staticmethod + def get_default_oss_storage( + workspace_id: str, cred: CredentialClient, region_id: str, network: Network + ) -> Tuple[Optional[str], Optional[str]]: + acs_ws_client = ClientFactory.create_client( + service_name=ServiceName.PAI_WORKSPACE, + credential_client=cred, + region_id=region_id, + network=network, + ) + workspace_api = WorkspaceAPI( + acs_client=acs_ws_client, + ) + resp = workspace_api.list_configs( + workspace_id=workspace_id, + config_keys=WorkspaceConfigKeys.DEFAULT_OSS_STORAGE_URI, + ) + oss_storage_uri = next( + ( + item["ConfigValue"] + for item in resp["Configs"] + if item["ConfigKey"] == WorkspaceConfigKeys.DEFAULT_OSS_STORAGE_URI + ), + None, + ) + + # Default OSS storage uri is not set. + if not oss_storage_uri: + return None, None + uri_obj = OssUriObj(oss_storage_uri) + if network == Network.VPC: + endpoint = "oss-{}-internal.aliyuncs.com".format(region_id) + else: + endpoint = "oss-{}.aliyuncs.com".format(region_id) + return uri_obj.bucket_name, endpoint + + @staticmethod + def _get_default_credential_client() -> Optional[CredentialClient]: + try: + # Initialize the credential client with default credential chain. + # see: https://help.aliyun.com/zh/sdk/developer-reference/v2-manage-python-access-credentials#3ca299f04bw3c + return CredentialClient() + except CredentialException: + return + + @staticmethod + def _get_workspace_id_by_dsw_instance_id( + dsw_instance_id: str, cred: CredentialClient, region_id: str, network: Network + ) -> Optional[str]: + """Get workspace id by dsw instance id""" + dsw_client = ClientFactory.create_client( + service_name=ServiceName.PAI_DSW, + credential_client=cred, + region_id=region_id, + network=network, + ) + try: + resp = dsw_client.get_instance(dsw_instance_id) + return resp.body.workspace_id + except TeaException as e: + logger.warning("Failed to get instance info by dsw instance id: %s", e) + return diff --git a/pai/toolkit/config.py b/pai/toolkit/config.py index 538ad97..a03465a 100644 --- a/pai/toolkit/config.py +++ b/pai/toolkit/config.py @@ -392,7 +392,7 @@ def workspace_choice_name(workspace: Dict[str, Any]): def prompt_for_oss_bucket(user_profile: UserProfile, workspace_id: str): - default_storage_uri = user_profile.get_default_oss_storage_uri( + default_storage_uri, endpoint = user_profile.get_default_oss_storage_uri( workspace_id=workspace_id ) print( @@ -667,7 +667,7 @@ def prompt_config_with_default_dsw_role(user_profile: UserProfile): ) ) - default_storage_uri = user_profile.get_default_oss_storage_uri( + default_storage_uri, endpoint = user_profile.get_default_oss_storage_uri( workspace_id=workspace_id, ) @@ -687,7 +687,6 @@ def prompt_config_with_default_dsw_role(user_profile: UserProfile): bucket_name, endpoint = None, None else: bucket_name = OssUriObj(default_storage_uri).bucket_name - endpoint = f"oss-{user_profile.region_id}-internal.aliyuncs.com" return workspace_id, bucket_name, endpoint diff --git a/pai/toolkit/helper/utils.py b/pai/toolkit/helper/utils.py index 2089ccb..1a93eef 100644 --- a/pai/toolkit/helper/utils.py +++ b/pai/toolkit/helper/utils.py @@ -15,7 +15,7 @@ import locale import os import re -from typing import Any, Dict, List, Optional +from typing import Any, Dict, List, Optional, Tuple import oss2 from alibabacloud_credentials.client import Client as CredentialClient @@ -41,6 +41,7 @@ from ...common.oss_utils import CredentialProviderWrapper, OssUriObj from ...common.utils import is_domain_connectable, make_list_resource_iterator from ...libs.alibabacloud_pai_dsw20220101.client import Client as DswClient +from ...session import Session logger = get_logger(__name__) @@ -111,7 +112,7 @@ def __init__( else: self.network = ( Network.VPC - if is_domain_connectable(PAI_VPC_ENDPOINT) + if is_domain_connectable(PAI_VPC_ENDPOINT.format(self.region_id)) else Network.PUBLIC ) self._caller_identify = self._get_caller_identity() @@ -137,9 +138,11 @@ def _get_caller_identity(self) -> CallerIdentity: config=open_api_models.Config( credential=self._get_credential_client(), region_id=self.region_id, - network=None - if self.network == Network.PUBLIC - else self.network.value.lower(), + network=( + None + if self.network == Network.PUBLIC + else self.network.value.lower() + ), ) ) .get_caller_identity() @@ -261,26 +264,15 @@ def get_workspace_api(self) -> WorkspaceAPI: acs_client=acs_ws_client, ) - def get_default_oss_storage_uri(self, workspace_id: str): - workspace_api = self.get_workspace_api() - resp = workspace_api.list_configs( + def get_default_oss_storage_uri( + self, workspace_id: str + ) -> Tuple[Optional[str], Optional[str]]: + return Session._get_default_oss_storage( workspace_id=workspace_id, - config_keys=WorkspaceConfigKeys.DEFAULT_OSS_STORAGE_URI, - ) - - oss_storage_uri = next( - ( - item["ConfigValue"] - for item in resp["Configs"] - if item["ConfigKey"] == WorkspaceConfigKeys.DEFAULT_OSS_STORAGE_URI - ), - None, + cred=self._get_credential_client(), + region_id=self.region_id, + network=self.network, ) - if not oss_storage_uri: - return - - uri_obj = OssUriObj(oss_storage_uri) - return "oss://{}".format(uri_obj.bucket_name) def set_default_oss_storage( self, workspace_id, bucket_name: str, intranet_endpoint: str diff --git a/pai/version.py b/pai/version.py index 7764bf3..b669ad5 100644 --- a/pai/version.py +++ b/pai/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -VERSION = "0.4.9.post0" +VERSION = "0.4.10.dev0" From 0c12db69c47eb5fd73519b40892b94327c06f012 Mon Sep 17 00:00:00 2001 From: LiangQuan Date: Thu, 22 Aug 2024 13:00:12 +0800 Subject: [PATCH 42/59] doc: remove tutorial notebook, add model recipe document (#39) * feat: estimator support config output channel value * fix: ensure the workspace_id is type string * doc: remove tutorial example * doc: add model_training_recipe document --- README.md | 26 +- README_EN.md | 33 +- docs/source/index.rst | 10 - docs/source/tutorial/.gitignore | 9 - docs/source/tutorial/advance.rst | 10 - .../async_inference/async_inference.ipynb | 464 ------ .../tutorial/baichuan2_finetune/.gitignore | 1 - .../baichuan2_finetune.ipynb | 292 ---- .../chatglm2_finetune/chatglm2_finetune.ipynb | 920 ------------ .../resource/gradio-chatglml.jpg | Bin 206542 -> 0 bytes .../tutorial/checkpoint/checkpoint.ipynb | 1285 ----------------- docs/source/tutorial/framework.rst | 17 - .../huggingface_bert/huggingface_bert.ipynb | 848 ----------- .../huggingface_model_deploy.ipynb | 181 --- .../model_deploy_container.ipynb | 376 ----- .../modelscope_model_deploy.ipynb | 216 --- .../modelscope_vit/modelscope_vit.ipynb | 638 -------- docs/source/tutorial/predict.rst | 12 - .../pretrained-model/pretrained-model.ipynb | 297 ---- .../tutorial/pytorch_ddp/pytorch_ddp.ipynb | 330 ----- .../tutorial/pytorch_ddp/resource/ddp.png | Bin 24040 -> 0 bytes .../pytorch_mnist/pytorch_mnist.ipynb | 969 ------------- .../resource/dreambooth.jpeg | Bin 617270 -> 0 bytes .../stable_diffusion_lora.ipynb | 750 ---------- .../stable_diffusion_lora/train-data/cat1.jpg | Bin 27340 -> 0 bytes .../stable_diffusion_lora/train-data/cat2.jpg | Bin 35903 -> 0 bytes .../stable_diffusion_lora/train-data/cat3.jpg | Bin 44470 -> 0 bytes .../train-data/metadata.jsonl | 3 - .../tutorial/tensorboard/tensorboard.ipynb | 245 ---- .../tensorflow_image_classification.ipynb | 797 ---------- docs/source/tutorial/train.rst | 11 - .../xgboost_breast_cancer.ipynb | 529 ------- docs/source/user-guide/pretrained-model.rst | 141 +- pai/estimator.py | 18 +- pai/session.py | 2 +- tests/integration/test_estimator.py | 33 + 36 files changed, 142 insertions(+), 9321 deletions(-) delete mode 100644 docs/source/tutorial/.gitignore delete mode 100644 docs/source/tutorial/advance.rst delete mode 100644 docs/source/tutorial/async_inference/async_inference.ipynb delete mode 100644 docs/source/tutorial/baichuan2_finetune/.gitignore delete mode 100644 docs/source/tutorial/baichuan2_finetune/baichuan2_finetune.ipynb delete mode 100644 docs/source/tutorial/chatglm2_finetune/chatglm2_finetune.ipynb delete mode 100644 docs/source/tutorial/chatglm_finetune/resource/gradio-chatglml.jpg delete mode 100644 docs/source/tutorial/checkpoint/checkpoint.ipynb delete mode 100644 docs/source/tutorial/framework.rst delete mode 100644 docs/source/tutorial/huggingface_bert/huggingface_bert.ipynb delete mode 100644 docs/source/tutorial/huggingface_model_deploy/huggingface_model_deploy.ipynb delete mode 100644 docs/source/tutorial/model_deploy_container/model_deploy_container.ipynb delete mode 100644 docs/source/tutorial/modelscope_model_deploy/modelscope_model_deploy.ipynb delete mode 100644 docs/source/tutorial/modelscope_vit/modelscope_vit.ipynb delete mode 100644 docs/source/tutorial/predict.rst delete mode 100644 docs/source/tutorial/pretrained-model/pretrained-model.ipynb delete mode 100644 docs/source/tutorial/pytorch_ddp/pytorch_ddp.ipynb delete mode 100644 docs/source/tutorial/pytorch_ddp/resource/ddp.png delete mode 100644 docs/source/tutorial/pytorch_mnist/pytorch_mnist.ipynb delete mode 100644 docs/source/tutorial/stable_diffusion_lora/resource/dreambooth.jpeg delete mode 100644 docs/source/tutorial/stable_diffusion_lora/stable_diffusion_lora.ipynb delete mode 100644 docs/source/tutorial/stable_diffusion_lora/train-data/cat1.jpg delete mode 100644 docs/source/tutorial/stable_diffusion_lora/train-data/cat2.jpg delete mode 100644 docs/source/tutorial/stable_diffusion_lora/train-data/cat3.jpg delete mode 100644 docs/source/tutorial/stable_diffusion_lora/train-data/metadata.jsonl delete mode 100644 docs/source/tutorial/tensorboard/tensorboard.ipynb delete mode 100644 docs/source/tutorial/tensorflow_image_classification/tensorflow_image_classification.ipynb delete mode 100644 docs/source/tutorial/train.rst delete mode 100644 docs/source/tutorial/xgboost_breast_cancer/xgboost_breast_cancer.ipynb diff --git a/README.md b/README.md index 5306161..494e2c0 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,31 @@ print(res.choices[0].message.content) ``` -更多功能介绍,请参阅 [PAI Python SDK文档](https://alipai.readthedocs.io/) 。 +- 微调预训练模型 + +通过PAI提供的微调脚本,提交一个模型微调任务 + +```python + +from pai.model import ModelTrainingRecipe + +training_recipe = ModelTrainingRecipe( + model_name="qwen2-0.5b-instruct", + model_provider="pai", + instance_type="ecs.gn6e-c12g1.3xlarge", +) + +training_recipe.train( + inputs={ + # 本地或是阿里云OSS上的数据路径(oss:///path/to/data) + "train": "" + } +) + + +``` + +通过访问PAI提供的示例仓库,可以了解更多使用示例:[pai-examples](https://github.com/aliyun/pai-examples/tree/master/pai-python-sdk) ## 🤝 贡献代码 diff --git a/README_EN.md b/README_EN.md index 833f99d..4268bcb 100644 --- a/README_EN.md +++ b/README_EN.md @@ -81,7 +81,38 @@ print(res.choices[0].message.content) ``` -For more details, please refer to the [PAI Python SDK Documentation](https://alipai.readthedocs.io/). +- Fine-tune the pretrained model +- +Submit a model fine-tuning task using the fine-tuning script provided by PAI. + +```python + +from pai.model import ModelTrainingRecipe + +# Retrieve the Qwen2-0.5b-instruct model training recipe provided by PAI +training_recipe = ModelTrainingRecipe( + model_name="qwen2-0.5b-instruct", + model_provider="pai", + instance_type="ecs.gn6e-c12g1.3xlarge", +) + +# Submit the training job +job = training_recipe.train( + inputs={ + # Data path on local or Alibaba Cloud OSS (oss:///path/to/data) + "train": "" + } +) + +# Get output model path +print(training_recipe.model_data()) + +# Deploy the fine-tuned model +predictor = training_recipe.deploy(service_name="qwen2_finetune") + +``` + +You can learn more usage examples by visiting the PAI example repository: [pai-examples](https://github.com/aliyun/pai-examples/tree/master/pai-python-sdk) ## 🤝 Contributing diff --git a/docs/source/index.rst b/docs/source/index.rst index f426f83..1ba224f 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -30,16 +30,6 @@ PAI Python SDK 文档 user-guide/processing-job -.. toctree:: - :maxdepth: 1 - :caption: 示例教程 - - tutorial/framework - tutorial/train - tutorial/predict - tutorial/advance - - .. toctree:: :maxdepth: 1 :caption: Reference diff --git a/docs/source/tutorial/.gitignore b/docs/source/tutorial/.gitignore deleted file mode 100644 index 879843a..0000000 --- a/docs/source/tutorial/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -test_data -train_data -xgb_src -train_src -infer_src -tf_train_src -fashion-mnist -data -bert diff --git a/docs/source/tutorial/advance.rst b/docs/source/tutorial/advance.rst deleted file mode 100644 index fbeebce..0000000 --- a/docs/source/tutorial/advance.rst +++ /dev/null @@ -1,10 +0,0 @@ -=========================================== -AIGC && LLM -=========================================== - -.. toctree:: - :maxdepth: 1 - - stable_diffusion_lora/stable_diffusion_lora - chatglm2_finetune/chatglm2_finetune - baichuan2_finetune/baichuan2_finetune diff --git a/docs/source/tutorial/async_inference/async_inference.ipynb b/docs/source/tutorial/async_inference/async_inference.ipynb deleted file mode 100644 index b28c169..0000000 --- a/docs/source/tutorial/async_inference/async_inference.ipynb +++ /dev/null @@ -1,464 +0,0 @@ -{ - "cells": [ - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# 部署异步推理服务\n", - "\n", - "在复杂的模型推理场景中,例如AIGC、视频处理等场景中,模型服务推理耗时较长,存在长连接超时导致请求失败或实例负载不均衡等问题,不适用于实时推理的场景。针对以上问题,PAI提供了异步推理服务,用于支持类似的场景,用户可以在提交预测请求之后,通过轮询或是订阅的方式获取到推理服务的预测结果。\n", - "\n", - "在当前文档中,我们将介绍如何使用PAI Python SDK在PAI上部署和调用异步推理服务。" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 准备工作\n", - "\n", - "我们可以通过以下命令安装PAI Python SDK。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [ - "skip-execution" - ] - }, - "outputs": [], - "source": [ - "\n", - "!python -m pip install --upgrade alipai" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "SDK需要配置访问阿里云服务需要的 AccessKey,以及当前使用的工作空间和OSS Bucket。在PAI Python SDK安装之后,通过在 **命令行终端** 中执行以下命令,按照引导配置密钥,工作空间等信息。\n", - "\n", - "\n", - "```shell\n", - "\n", - "# 以下命令,请在 命令行终端 中执行.\n", - "\n", - "python -m pai.toolkit.config\n", - "\n", - "```\n", - "\n", - "我们可以通过以下代码验证当前的配置。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import pai\n", - "from pai.session import get_default_session\n", - "\n", - "print(pai.__version__)\n", - "sess = get_default_session()\n", - "\n", - "assert sess.workspace_name is not None\n", - "print(sess.workspace_name)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 部署异步推理服务模型\n", - "\n", - "将模型部署为异步推理服务与部署标准的在线推理服务类似,用户仅需在部署时(`Model.deploy`),传递`service_type=ServicType.Async`即可。\n", - "\n", - "当前流程中,我们将使用镜像部署的模式,部署一个异步的推理服务。\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "\n", - "# 准备异步推理服务的应用代码目录\n", - "!mkdir -p serve_src/" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "通过`%%writefile`指令,我们将推理服务代码写入到`serve_src/run.py`文件中。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%%writefile serve_src/run.py\n", - "import asyncio\n", - "from random import random\n", - "\n", - "from fastapi import FastAPI, Request\n", - "import uvicorn, json, datetime\n", - "\n", - "# 默认模型加载路径\n", - "model_path = \"/eas/workspace/model/\"\n", - "\n", - "app = FastAPI()\n", - "\n", - "\n", - "@app.post(\"/\")\n", - "async def create_item(request: Request):\n", - " print(\"Make mock prediction starting ...\")\n", - " # Mock prediction\n", - " await asyncio.sleep(15)\n", - " print(\"Prediction finished.\")\n", - " return [random() for _ in range(10)]\n", - "\n", - "\n", - "if __name__ == \"__main__\":\n", - " uvicorn.run(app, host=\"0.0.0.0\", port=8000, workers=1)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "我们将使用PAI提供的PyTorch推理镜像部署以上的模型。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pai.model import Model, container_serving_spec, ServiceType\n", - "from pai.image import retrieve, ImageScope\n", - "\n", - "m = Model(\n", - " inference_spec=container_serving_spec(\n", - " source_dir=\"serve_src\",\n", - " command=\"python run.py\",\n", - " image_uri=retrieve(\n", - " \"PyTorch\",\n", - " framework_version=\"1.10\",\n", - " accelerator_type=\"gpu\",\n", - " image_scope=ImageScope.INFERENCE,\n", - " ),\n", - " requirements=[\n", - " \"fastapi\",\n", - " \"uvicorn\",\n", - " ],\n", - " )\n", - " # 用户可以通过`model_data`参数,传递一个OSS上的模型。相应的模型会被加载到推理服务的容器中。\n", - " # model_data=\"oss:///path/to/model/\"\n", - ")" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "通过设置部署服务的`service_type=ServiceType.Async`参数,我们可以将模型部署为异步推理服务。异步推理服务使用分别使用输入队列(source)和输出队列(sink)保存预测请求和预测结果。通过`options`参数,可以配置队列使用的资源,队列最大长度,是否开启自动驱逐等高阶参数。异步服务支持的完整的高阶参数,请参考文档:[异步服务-参数配置](https://help.aliyun.com/document_detail/476812.html?#section-gor-gne-gtq)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pai.predictor import AsyncPredictor\n", - "from pai.common.utils import random_str\n", - "\n", - "\n", - "service_name = f\"async_service_example_{random_str(6)}\"\n", - "\n", - "p: AsyncPredictor = m.deploy(\n", - " service_name=service_name,\n", - " instance_type=\"ecs.c6.large\",\n", - " # 设置当前部署的服务类型为异步服务\n", - " service_type=ServiceType.Async,\n", - " # 用户可以通过options字段配置高阶参数\n", - " options={\n", - " # 异步推理详细参数文档: https://help.aliyun.com/document_detail/476812.html\n", - " \"queue.cpu\": 2, # 队列使用的CPU核数,默认为1\n", - " \"queue.memory\": 2048, # 异步服务使用过的队列内存,单位为MB\n", - " },\n", - ")\n", - "\n", - "print()\n", - "\n", - "print(p)\n", - "print(p.service_name)\n", - "print(p.access_token)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 调用推理服务\n", - "\n", - "用户发送调用异步队列服务与请求同步推理服务的方式相同,但是异步推理服务会立即返回本次预测请求的`RequestId`,而不是预测结果。用户可以通过轮询获取到推理服务的预测结果。\n", - "\n", - "- **用户客户端**发送推理请求,加入到推理服务的输入队列中,PAI-EAS返回请求的RequestId。\n", - "- PAI处理输入队列中的请求,转发给到**用户的推理服务**,推理服务处理完请求后,将结果写入到输出队列中\n", - "- **用户客户端**可以通过RequestId轮询,可以获取到**用户推理服务**的预测结果\n", - "\n", - "\n", - "PAI Python SDK提供了`AsyncPredictor`,支持用户更加简单得调用异步推理服务。" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 调用异步推理服务\n", - "\n", - "`AsyncPredictor`提供了`predict`和`raw_predict`方法发送预测请求,它们都会返回一个`AsyncTask`,用户可以通过`AsyncTask.result()`获取预测结果。 \n", - "\n", - "二者的区别在于`predict`方法会使用`Serializer`对象对输入数据进行序列化,对预测结果进行反序列化,而`raw_predict`方法直接将输入数据发送给异步推理服务,返回HTTP响应结果(`RawResponse`)。\n", - "\n", - "```python\n", - "\n", - "from pai.predictor import AsyncPredictor, AsyncTask\n", - "from pai.serializer import JsonSerializer\n", - "\n", - "p = AsyncPredictor(service_name='test_async_service', serializer=JsonSerializer())\n", - "\n", - "t1: AsyncTask = p.predict(data={\"some\": \"data\"})\n", - "# result是推理服务的响应结果(Response Body),经过Serialzier.deserialize处理后返回的结果.\n", - "result = t1.result()\n", - "\n", - "\n", - "t2: AsyncTask = p.raw_predict(data=b'{\"some\": \"data\"}')\n", - "resp: RawResponse = t2.result()\n", - "print(resp.status_code, resp.content)\n", - "\n", - "```\n", - "\n", - "`AsyncPredictor`会维护一个线程池,通过一个线程去发送推理请求,并等待请求处理完成。用户可以通过`max_workers`参数配置线程池的大小。\n", - "\n", - "```python\n", - "\n", - "p = AsyncPredictor(service_name='test_async_service', max_workers=20)\n", - "\n", - "```\n", - "\n", - "当用户需要在异步请求完成之后,对于响应的结果进行处理时,可以通过`callback`参数传递一个回调函数。回调函数的参数为`AsyncTask.result()`,也就实际响应的结果。\n", - "\n", - "\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "以下的示例代码中,我们将使用`AsyncPredictor`调用异步推理服务,并通过会回调函数处理预测结果。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pai.predictor import RawResponse, AsyncTask\n", - "import time\n", - "\n", - "# 结果列表\n", - "results = []\n", - "\n", - "\n", - "# 定义回调函数\n", - "def callback_fn(resp: RawResponse):\n", - " print(\"Callback: get prediction result \", resp.json())\n", - " results.append(resp.json())\n", - "\n", - "\n", - "# 发送预测请求,使用回调函数处理预测结果。\n", - "task: AsyncTask = p.raw_predict(\n", - " data=b\"test_data\",\n", - " callback=callback_fn,\n", - ")\n", - "\n", - "# result() 方法等待预测完成\n", - "resp: RawResponse = task.result()\n", - "print(resp.json())\n", - "\n", - "# 等待回调函数执行完成\n", - "time.sleep(1)\n", - "\n", - "print(results)\n", - "assert len(results) == 1" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "以下示例中,我们批量发送异步推理请求,然后等待所有的请求完成。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "tasks = []\n", - "\n", - "for i in range(10):\n", - " task: AsyncTask = p.raw_predict(\n", - " data=b\"test_data\",\n", - " callback=lambda x: print(\"Prediction result: \", x.json()),\n", - " )\n", - " tasks.append(task)\n", - "\n", - "prediction_results = [t.result().json() for t in tasks]\n", - "\n", - "print(prediction_results)\n", - "print(len(prediction_results))" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 使用异步API调用推理服务\n", - "\n", - "`AsyncPredictor` 提供了异步API `raw_predict_async` 和 `predict_async`,支持用户使用Python提供的异步框架(asyncio)调用推理服务。\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pai.predictor import RawResponse\n", - "\n", - "# 使用异步API调用异步推理服务\n", - "res: RawResponse = await p.raw_predict_async(data=b\"test_data\")\n", - "\n", - "print(res.status_code)\n", - "print(res.content)\n", - "print(res.json())" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "通过SDK提供的异步API,我们可以不借助于线程池,批量发送异步预测请求。\n", - "\n", - "以下的示例中,我们将使用异步API,批量发送异步预测请求,等待推理完成,并使用回调函数打印预测请求结果。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import asyncio\n", - "\n", - "\n", - "# 定义回调函数\n", - "def task_done_cb(task: asyncio.Task):\n", - " if task.exception():\n", - " raise task.exception()\n", - " else:\n", - " print(\"Prediction result: \", task.result().json())\n", - "\n", - "\n", - "# 使用异步API批量调用异步推理服务\n", - "async def batch_predict():\n", - " tasks = []\n", - " for _ in range(10):\n", - " task = asyncio.create_task(\n", - " # raw_predict_async 是一个coroutine\n", - " p.raw_predict_async(\n", - " data=b\"test_data\",\n", - " )\n", - " )\n", - " # 调用完成之后,打印调用返回结果\n", - " task.add_done_callback(task_done_cb)\n", - "\n", - " tasks.append(task)\n", - " # 等待所有任务完成\n", - " return await asyncio.gather(*tasks, return_exceptions=True)\n", - "\n", - "\n", - "batch_results = await batch_predict()\n", - "\n", - "\n", - "for result in batch_results:\n", - " print(result.json())" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "测试完成之后,可以使用`delete_service`方法删除对应服务,释放资源。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "p.delete_service()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "pai-dev-py38", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.16" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/source/tutorial/baichuan2_finetune/.gitignore b/docs/source/tutorial/baichuan2_finetune/.gitignore deleted file mode 100644 index f23b395..0000000 --- a/docs/source/tutorial/baichuan2_finetune/.gitignore +++ /dev/null @@ -1 +0,0 @@ -swift diff --git a/docs/source/tutorial/baichuan2_finetune/baichuan2_finetune.ipynb b/docs/source/tutorial/baichuan2_finetune/baichuan2_finetune.ipynb deleted file mode 100644 index d7b2576..0000000 --- a/docs/source/tutorial/baichuan2_finetune/baichuan2_finetune.ipynb +++ /dev/null @@ -1,292 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# 使用ModelScope Swift微调Baichuan2模型" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 介绍\n", - "\n", - "[Baichuan 2](https://github.com/baichuan-inc/Baichuan2)是[百川智能](https://www.baichuan-ai.com/home)推出的开源大语言模型,采用2.6万亿Tokens的高质量语料进行训练,在多个权威的中文、英文和多语言的通用、领域benchmark上取得了同尺寸最佳的效果。`Baichuan2` 目前发布了7B、13B的Base和Chat版本,支持模型商用。\n", - "\n", - "当在特定领域使用大语言模型时,可以通过prompt的方式引导模型,也可以通过在领域数据集上微调训练,从而在领域的任务上获得更好的效果。后者的优点是不依赖于Prompt(可能超过模型的输入长度上限),有更好的推理性能,并且经过微调后,在领域相关任务上有更好的效果。\n", - "\n", - "本文将介绍如何在PAI对`Baichuan2`模型完成微调训练。" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "## 安装和配置SDK\n", - "\n", - "我们需要首先安装PAI Python SDK以运行本示例。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "\n", - "!python -m pip install --upgrade alipai" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "\n", - "SDK需要配置访问阿里云服务需要的AccessKey,以及当前使用的工作空间和OSS Bucket。在PAI SDK安装之后,通过在**命令行终端** 中执行以下命令,按照引导配置密钥、工作空间等信息。\n", - "\n", - "\n", - "```shell\n", - "\n", - "# 以下命令,请在 命令行终端 中执行.\n", - "\n", - "python -m pai.toolkit.config\n", - "\n", - "```\n", - "\n", - "我们可以通过以下代码验证配置是否已生效。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import pai\n", - "from pai.session import get_default_session\n", - "\n", - "print(pai.__version__)\n", - "\n", - "sess = get_default_session()\n", - "\n", - "# 获取配置的工作空间信息\n", - "assert sess.workspace_name is not None\n", - "print(sess.workspace_name)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 准备训练脚本\n", - "\n", - "`ModelScope`提供了[SWIFT(Scalable lightWeight Infrastructure for Fine-Tuning)](https://github.com/modelscope/swift#swiftscalable-lightweight-infrastructure-for-fine-tuning)框架,支持模型的全参数微调,也集成了各种高效微调方法,例如`LoRA`、`QLoRA`等,支持用户对`Baichuan2`、`QWen`、`llama2`等常见的语言进行微调训练。\n", - "\n", - "基于[Swift的LLM finetune脚本](https://github.com/modelscope/swift/blob/main/examples/pytorch/llm/src/llm_sft.py),我们修改了部分逻辑,从而支持用户在PAI的训练作业中使用,主要包括:\n", - "\n", - "- 使用PAI预置的`Baichuan2-Base`模型\n", - "\n", - "对于热门的社区模型,PAI提供了模型缓存在OSS Bucket上,支持挂载到训练作业,训练脚本可以通过读取本地文件的方式加载获取模型。\n", - "\n", - "- 保存模型\n", - "\n", - "训练脚本需要将模型保存到指定路径(`/ml/output/model`),从而将模型保存到用户的OSS Bucket中。\n", - "\n", - "- 训练依赖的第三方\n", - "\n", - "训练作业将运行在PAI提供的`PyTorch`基础镜像上,我们需要在作业环境中安装`transformers`、`datasets`、`swift`、`xformers`等第三方依赖。PAI训练作业支持使用训练脚本目录下的`requirements.txt`安装第三方依赖。\n", - "\n", - "\n", - "完整的训练脚本请参考 `train_src` 目录下的训练文件(`llm_sft.py`)。\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 提交训练作业\n", - "\n", - "使用提交任务的方式训练模型,能够支持用户并行运行多个训练任务,高效得探索不同的超参组合对于模型性能影响,并且能够支持分布式训练。通过PAI Python SDK提供的`Estimator`API,我们可以方便得将一个本地训练脚本提交到PAI上运行。\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "我们将通过以下代码配置训练作业脚本、作业启动命令、使用的作业镜像,以及机器实例规格,提交训练作业。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pai.image import retrieve\n", - "from pai.estimator import Estimator\n", - "\n", - "# 训练作业启动命令\n", - "# 完整的参数说明请参考文档:https://github.com/modelscope/swift/blob/main/examples/pytorch/llm/README_CN.md#sftsh-%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%8F%82%E6%95%B0\n", - "command = \"\"\"CUDA_VISIBLE_DEVICES=0 \\\n", - "python llm_sft.py \\\n", - " --model_type baichuan2-7b \\\n", - " --sft_type lora \\\n", - " --template_type default-generation \\\n", - " --dtype fp16 \\\n", - " --output_dir /ml/output/model/ \\\n", - " --dataset advertise-gen \\\n", - " --train_dataset_sample 20000 \\\n", - " --num_train_epochs 1 \\\n", - " --max_length 2048 \\\n", - " --quantization_bit 4 \\\n", - " --lora_rank 8 \\\n", - " --lora_alpha 32 \\\n", - " --lora_dropout_p 0. \\\n", - " --lora_target_modules ALL \\\n", - " --gradient_checkpointing true \\\n", - " --batch_size 16 \\\n", - " --weight_decay 0. \\\n", - " --learning_rate 1e-4 \\\n", - " --gradient_accumulation_steps 4 \\\n", - " --max_grad_norm 0.5 \\\n", - " --warmup_ratio 0.03 \\\n", - " --eval_steps 100 \\\n", - " --save_steps 100 \\\n", - " --save_total_limit 2 \\\n", - " --logging_steps 10\n", - "\"\"\"\n", - "\n", - "\n", - "# 配置训练作业\n", - "est = Estimator(\n", - " source_dir=\"train_src/\", # 代码目录\n", - " image_uri=retrieve(\"PyTorch\", framework_version=\"latest\").image_uri, # 训练作业使用的镜像\n", - " command=command, # 训练启动命令\n", - " instance_type=\"ecs.gn6e-c12g1.3xlarge\", # 使用的机器规格示例,V100(32G)\n", - " instance_count=1, # 机器实例个数\n", - " base_job_name=\"baichuan2_finetune\", # 训练作业名称\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "PAI提供了预置的`Baichuan2-Base`模型,可以通过以下方式获取对应的模型`OSS Bucket`路径。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pai.model import RegisteredModel\n", - "\n", - "# 获取PAI提供的Baichuan2-7B-Base模型\n", - "m = RegisteredModel(\n", - " model_name=\"baichuan-inc/Baichuan2-7B-Base\", model_provider=\"huggingface\"\n", - ")\n", - "\n", - "# 模型地址\n", - "print(m.model_data)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "提交训练作业,等待作业完成。用户可以通过打印的作业详情页URL,查看训练作业进度,资源使用,日志等信息。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "notebookRunGroups": { - "groupValue": "2" - } - }, - "outputs": [], - "source": [ - "# 提交训练作业\n", - "est.fit(\n", - " inputs={\n", - " # 训练代码可以从 /ml/input/data/pretrained_model/ 目录下读取挂载的预训练模型\n", - " \"pretrained_model\": m.model_data,\n", - " },\n", - " wait=False, # 是否等待训练作业完成\n", - ")\n", - "\n", - "# 打开一个TensorBoard,监控训练作业\n", - "tb = est.tensorboard()\n", - "\n", - "\n", - "# 等待训练作业完成\n", - "est.wait()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "训练作业写出到 `/ml/output/model` 目录下的模型文件和checkpoints将被保存到用户的OSS Bucket中,可以通过 `est.model_data()` 获取 OSS Bucket路径。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# 查看数据模型的OSS Bucket路径\n", - "print(est.model_data())\n", - "\n", - "\n", - "# 删除启动的TensorBoard(每一个账号下最多能够启动5个TensorBoard示例)\n", - "tb.delete()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 结语\n", - "\n", - "在当前示例中,我们展示了如何基于`ModelScope Swift`框架,使用PAI预置的`Baichuan2-Base`模型,完成`Baichuan2`模型的微调训练。用户可以参考以上的示例,修改脚本,使用用户自定义的数据集,或是修改使用的基础预训练模型,完成自定义语言模型的微调训练。\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "pai-dev-py38", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.16" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/source/tutorial/chatglm2_finetune/chatglm2_finetune.ipynb b/docs/source/tutorial/chatglm2_finetune/chatglm2_finetune.ipynb deleted file mode 100644 index db4cd46..0000000 --- a/docs/source/tutorial/chatglm2_finetune/chatglm2_finetune.ipynb +++ /dev/null @@ -1,920 +0,0 @@ -{ - "cells": [ - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# 微调和部署对话模型ChatGLM2-6B\n", - "\n", - "[ChatGLM2-6B](https://www.modelscope.cn/models/ZhipuAI/chatglm2-6b/summary)是中英文对话模型[ChatGLM-6B](https://github.com/THUDM/ChatGLM-6B) 的第二代版本,在保留了初代模型对话流畅、部署门槛较低等众多优秀特性的基础之上,ChatGLM2-6B 引入了多项升级,包括更强大的性能、更长的上下文、更高效的推理。\n", - "\n", - "在本示例中,我们将展示:\n", - "\n", - "- 将ChatGLM2-6B部署到PAI创建推理服务,基于推理服务API和Gradio实现一个简易对话机器人。\n", - "\n", - "- 在PAI对ChatGLM2-6B进行微调训练,并将微调的模型部署创建推理服务。\n", - "\n", - "\n", - "## 准备工作\n", - "\n", - "### 前提条件\n", - "\n", - "- 已获取阿里云账号的鉴权AccessKey ID和AccessKey Secret,详情请参见:[获取AccessKey](https://help.aliyun.com/document_detail/116401.html)。\n", - "- 已创建或是加入一个PAI AI工作空间,详情请参见:[创建工作空间](https://help.aliyun.com/document_detail/326193.html)。\n", - "- 已创建OSS Bucket,详情请参见:[控制台创建存储空间](https://help.aliyun.com/document_detail/31885.html)。\n", - "\n", - "\n", - "### 安装和配置PAI Python SDK\n", - "\n", - "我们将使用PAI提供的Python SDK,提交训练作业,部署模型。可以通过以下命令安装PAI Python SDK。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!python -m pip install --upgrade alipai\n", - "!python -m pip install gradio" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "SDK需要配置访问阿里云服务需要的 AccessKey,以及当前使用的工作空间和OSS Bucket。在PAI Python SDK安装之后,通过在 **命令行终端** 中执行以下命令,按照引导配置密钥,工作空间等信息。\n", - "\n", - "\n", - "```shell\n", - "\n", - "# 以下命令,请在 命令行终端 中执行.\n", - "\n", - "python -m pai.toolkit.config\n", - "\n", - "```\n", - "\n", - "我们可以通过以下代码验证当前的配置。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import pai\n", - "from pai.session import get_default_session\n", - "\n", - "print(pai.__version__)\n", - "sess = get_default_session()\n", - "\n", - "assert sess.workspace_name is not None\n", - "print(sess.workspace_name)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 直接部署ChatGLM2\n", - "\n", - "`ChatGLM2-6B`是一个对话语言模型,能够基于历史对话信息,和用户的Prompt输入,进行反馈。通过HuggingFace的transformers库用户可以直接使用`ChatGLM2-6B`提供的对话能力,示例如下:\n", - "\n", - "```python\n", - "\n", - ">>> from transformers import AutoTokenizer, AutoModel\n", - ">>> tokenizer = AutoTokenizer.from_pretrained(\"THUDM/chatglm2-6b\", trust_remote_code=True)\n", - ">>> model = AutoModel.from_pretrained(\"THUDM/chatglm2-6b\", trust_remote_code=True).half().cuda()\n", - ">>> model = model.eval()\n", - ">>> response, history = model.chat(tokenizer, \"你好\", history=[])\n", - ">>> print(response)\n", - "你好👋!我是人工智能助手 ChatGLM2-6B,很高兴见到你,欢迎问我任何问题。\n", - ">>> response, history = model.chat(tokenizer, \"晚上睡不着应该怎么办\", history=history)\n", - ">>> print(response)\n", - "晚上睡不着可能会让你感到焦虑或不舒服,但以下是一些可以帮助你入睡的方法:\n", - "\n", - "1. 制定规律的睡眠时间表:保持规律的睡眠时间表可以帮助你建立健康的睡眠习惯,使你更容易入睡。尽量在每天的相同时间上床,并在同一时间起床。\n", - "2. 创造一个舒适的睡眠环境:确保睡眠环境舒适,安静,黑暗且温度适宜。可以使用舒适的床上用品,并保持房间通风。\n", - "3. 放松身心:在睡前做些放松的活动,例如泡个热水澡,听些轻柔的音乐,阅读一些有趣的书籍等,有助于缓解紧张和焦虑,使你更容易入睡。\n", - "4. 避免饮用含有咖啡因的饮料:咖啡因是一种刺激性物质,会影响你的睡眠质量。尽量避免在睡前饮用含有咖啡因的饮料,例如咖啡,茶和可乐。\n", - "5. 避免在床上做与睡眠无关的事情:在床上做些与睡眠无关的事情,例如看电影,玩游戏或工作等,可能会干扰你的睡眠。\n", - "6. 尝试呼吸技巧:深呼吸是一种放松技巧,可以帮助你缓解紧张和焦虑,使你更容易入睡。试着慢慢吸气,保持几秒钟,然后缓慢呼气。\n", - "\n", - "如果这些方法无法帮助你入睡,你可以考虑咨询医生或睡眠专家,寻求进一步的建议。\n", - "\n", - "\n", - "\n", - "```\n", - "\n", - "以下的流程中,我们将`ChatGLM2-6B`部署到PAI创建一个推理服务,然后基于推理服务的API,使用Gradio创建一个对话机器人。" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "### 获取ChatGLM2模型\n", - "\n", - "推理服务和训练作业中都需要加载使用模型,PAI在部分region上提供模型缓存,支持用户能够更快地获取到相应的模型。用户可以通过以下代码获取相应的模型,然后在训练作业和推理服务中加载使用。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pai.model import RegisteredModel\n", - "\n", - "m = RegisteredModel(\n", - " \"THUDM/chatglm2-6b\",\n", - " model_provider=\"huggingface\",\n", - ")\n", - "\n", - "model_uri = m.model_data\n", - "print(model_uri)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 创建推理服务\n", - "\n", - "PAI-EAS是阿里云PAI提供模型在线服务平台,支持用户一键部署推理服务或是AIWeb应用,支持异构资源,弹性扩缩容。PAI-EAS支持使用镜像的方式部署模型,以下的流程,我们将使用PAI提供的PyTorch推理镜像,将以上的模型部署为推理服务。\n", - "\n", - "\n", - "在部署推理服务之前,我们需要准备相应的推理服务程序,他负责加载模型,提供对应的HTTP API服务。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!mkdir -p server_src" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "完整的推理服务代码如下:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%%writefile server_src/run.py\n", - "# source: https://github.com/THUDM/ChatGLM-6B/blob/main/api.py\n", - "\n", - "import os\n", - "\n", - "from fastapi import FastAPI, Request\n", - "from transformers import AutoTokenizer, AutoModel, AutoConfig\n", - "import uvicorn, json, datetime\n", - "import torch\n", - "\n", - "\n", - "model = None\n", - "tokenizer = None\n", - "\n", - "# 默认的模型保存路径\n", - "chatglm_model_path = \"/eas/workspace/model/\"\n", - "# ptuning checkpoints保存路径\n", - "ptuning_checkpoint = \"/ml/ptuning_checkpoints/\"\n", - "pre_seq_len = 128\n", - "app = FastAPI()\n", - "\n", - "\n", - "def load_model():\n", - " global model, tokenizer\n", - " tokenizer = AutoTokenizer.from_pretrained(chatglm_model_path, trust_remote_code=True)\n", - "\n", - " if os.path.exists(ptuning_checkpoint):\n", - " # P-tuning v2\n", - " print(f\"Loading model/ptuning_checkpoint weight...\")\n", - " config = AutoConfig.from_pretrained(chatglm_model_path, trust_remote_code=True)\n", - " config.pre_seq_len = pre_seq_len\n", - " config.prefix_projection = False\n", - "\n", - " model = AutoModel.from_pretrained(chatglm_model_path, config=config, trust_remote_code=True)\n", - " tokenizer = AutoTokenizer.from_pretrained(chatglm_model_path, trust_remote_code=True)\n", - " prefix_state_dict = torch.load(os.path.join(ptuning_checkpoint, \"pytorch_model.bin\"))\n", - " new_prefix_state_dict = {}\n", - " for k, v in prefix_state_dict.items():\n", - " if k.startswith(\"transformer.prefix_encoder.\"):\n", - " new_prefix_state_dict[k[len(\"transformer.prefix_encoder.\"):]] = v\n", - " model.transformer.prefix_encoder.load_state_dict(new_prefix_state_dict)\n", - "\n", - " model = model.half().cuda()\n", - " model.transformer.prefix_encoder.float().cuda()\n", - " model.eval()\n", - " else:\n", - " print(f\"Loading model weight...\")\n", - " model = AutoModel.from_pretrained(chatglm_model_path, trust_remote_code=True)\n", - " model.half().cuda()\n", - " model.eval()\n", - "\n", - "\n", - "\n", - "@app.post(\"/\")\n", - "async def create_item(request: Request):\n", - " global model, tokenizer\n", - " json_post_raw = await request.json()\n", - " json_post = json.dumps(json_post_raw)\n", - " json_post_list = json.loads(json_post)\n", - " prompt = json_post_list.get('prompt')\n", - " history = json_post_list.get('history')\n", - " max_length = json_post_list.get('max_length')\n", - " top_p = json_post_list.get('top_p')\n", - " temperature = json_post_list.get('temperature')\n", - " response, history = model.chat(tokenizer,\n", - " prompt,\n", - " history=history,\n", - " max_length=max_length if max_length else 2048,\n", - " top_p=top_p if top_p else 0.7,\n", - " temperature=temperature if temperature else 0.95)\n", - " now = datetime.datetime.now()\n", - " time = now.strftime(\"%Y-%m-%d %H:%M:%S\")\n", - " answer = {\n", - " \"response\": response,\n", - " \"history\": history,\n", - " \"status\": 200,\n", - " \"time\": time\n", - " }\n", - " log = \"[\" + time + \"] \" + '\", prompt:\"' + prompt + '\", response:\"' + repr(response) + '\"'\n", - " print(log)\n", - " return answer\n", - "\n", - "\n", - "if __name__ == '__main__':\n", - " load_model()\n", - " uvicorn.run(app, host='0.0.0.0', port=8000, workers=1)\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "我们将使用PyTorch镜像运行相应的推理服务,在启动服务之前需要安装模型依赖的相关的依赖。我们可以在`server_src`下准备依赖的`requirements.txt`,对应的`requirements.txt`会在推理服务启动之前被安装到环境中。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%%writefile server_src/requirements.txt\n", - "\n", - "# 模型需要的依赖\n", - "transformers==4.30.2\n", - "accelerate\n", - "icetk\n", - "cpm_kernels\n", - "\n", - "torch>=2.0,<2.1\n", - "gradio\n", - "mdtex2html\n", - "sentencepiece\n", - "accelerate\n", - "\n", - "# 推理服务Server的依赖\n", - "fastapi\n", - "uvicorn" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "基于以上的推理服务程序,我们将使用PyTorch镜像和OSS上的模型在PAI创建一个推理服务,代码如下。\n", - "\n", - "> 对于如何使用SDK创建推理服务的详细介绍,请见文档:[创建推理服务](https://help.aliyun.com/document_detail/2261532.html)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pai.model import container_serving_spec, Model\n", - "from pai.image import retrieve, ImageScope\n", - "from pai.common.utils import random_str\n", - "\n", - "\n", - "# InferenceSpec用于描述如何创建推理服务\n", - "infer_spec = container_serving_spec(\n", - " # 使用PAI提供的最新PyTorch的推理镜像\n", - " image_uri=retrieve(\n", - " \"PyTorch\",\n", - " \"latest\",\n", - " accelerator_type=\"GPU\",\n", - " image_scope=ImageScope.INFERENCE,\n", - " ),\n", - " source_dir=\"./server_src\",\n", - " command=\"python run.py\",\n", - ")\n", - "\n", - "m = Model(\n", - " # 模型的OSS路径,默认模型会通过挂载的方式挂载到`/eas/workspace/model/`路径下。\n", - " model_data=model_uri,\n", - " inference_spec=infer_spec,\n", - ")\n", - "\n", - "\n", - "# 部署模型,创建推理服务.\n", - "p = m.deploy(\n", - " service_name=\"chatglm_demo_{}\".format(random_str(6)),\n", - " instance_type=\"ecs.gn6i-c8g1.2xlarge\", # 8vCPU 31GB NVIDIA T4×1(GPU Mem 16GB)\n", - " options={\n", - " # 配置EAS RPC框架的超时时间, 单位为毫秒\n", - " \"metadata.rpc.keepalive\": 20000,\n", - " },\n", - ")\n", - "\n", - "print(p.service_name)\n", - "print(p.service_status)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`m.deploy`返回一个Predictor对象,可以用于向创建的推理服务程序发送预测请求。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pai.predictor import RawResponse\n", - "\n", - "resp: RawResponse = p.raw_predict(\n", - " {\n", - " \"prompt\": \"你好\",\n", - " }\n", - ")\n", - "print(resp.json()[\"response\"])\n", - "\n", - "\n", - "resp = p.raw_predict(\n", - " {\n", - " \"prompt\": \"晚上睡不着应该怎么办\",\n", - " \"history\": resp.json()[\"history\"],\n", - " },\n", - " timeout=20,\n", - ")\n", - "print(resp.json())" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "基于以上的推理服务,我们可以使用Gradio创建一个简单的对话机器人demo。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import gradio as gr\n", - "import random\n", - "import time\n", - "\n", - "with gr.Blocks() as demo:\n", - " chatbot = gr.Chatbot()\n", - " msg = gr.Textbox()\n", - " clear = gr.Button(\"Clear\")\n", - " submit = gr.Button(\"Submit\")\n", - "\n", - " def respond(message, chat_history):\n", - "\n", - " print(f\"Message: {message}\")\n", - " print(f\"ChatHistory: {chat_history}\")\n", - " resp = p.raw_predict(\n", - " {\n", - " \"prompt\": message,\n", - " \"history\": chat_history,\n", - " }\n", - " ).json()\n", - " print(f\"Response: {resp['response']}\")\n", - "\n", - " chat_history.append((message, resp[\"response\"]))\n", - " return \"\", chat_history\n", - "\n", - " submit.click(respond, [msg, chatbot], [msg, chatbot])\n", - "\n", - "demo.launch(share=True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "通过以上创建的Gradio应用,我们可以在页面上与部署的ChatGLM模型进行对话。\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "在测试完成之后,我们可以通过以下的代码删除推理服务,释放资源。\n", - "\n", - "> 请注意,删除在线推理服务之后,对应的Gradio的应用将无法使用。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "p.delete_service()" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 微调ChatGLM2-6B\n", - "\n", - "我们可以使用领域数据对ChatGLM进行微调,从而使得模型在特定领域和任务下有更好的表现。ChatGLM团队提供了使用[P-Tuning v2](https://github.com/THUDM/P-tuning-v2)方式对模型进行[微调的方案](https://github.com/THUDM/ChatGLM2-6B/tree/main/ptuning),我们将基于此方案展示如何将微调训练作业提交到PAI的训练服务执行。\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 准备训练数据集\n", - "\n", - "我们将使用了[广告生成数据集](https://aclanthology.org/D19-1321.pdf),对ChatGLM进行微调。我们首先需要准备数据到OSS,供后续微调训练作业使用。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pai.common.oss_utils import download, OssUriObj, upload\n", - "import zipfile\n", - "\n", - "# 下载数据\n", - "data = download(\n", - " # 当前的数据集在上海region,跨region下载,我们需要传递对应OSS Bucket所在Endpoint.\n", - " OssUriObj(\n", - " \"oss://atp-modelzoo-sh.oss-cn-shanghai.aliyuncs.com/release/tutorials/chatGLM/AdvertiseGen_Simple.zip\"\n", - " ),\n", - " local_path=\"./\",\n", - ")\n", - "\n", - "# 解压缩数据\n", - "with zipfile.ZipFile(data, \"r\") as zip_ref:\n", - " zip_ref.extractall(\"./train_data/\")\n", - "\n", - "# 上传数据到OSS\n", - "train_data = \"./train_data/AdvertiseGen_Simple/\"\n", - "train_data_uri = upload(\n", - " \"./train_data/AdvertiseGen_Simple/\", oss_path=\"chatglm_demo/data/advertisegen/\"\n", - ")\n", - "print(train_data_uri)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "相应的数据集数据格式如下:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!head -n 5 ./train_data/AdvertiseGen_Simple/train.json" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 准备微调训练作业脚本\n", - "\n", - "ChatGLM的官方提供[微调训练脚本](https://github.com/THUDM/ChatGLM2-6B/tree/main/ptuning),支持使用P-Tuning v2的方式对ChatGLM模型进行微调。我们将基于相应的微调训练脚本,修改训练作业的拉起Shell脚本(`train.sh`),然后使用PAI Python SDK将微调训练作业提交到PAI执行。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# 下载ChatGLM代码\n", - "!git clone https://github.com/THUDM/ChatGLM2-6B.git" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "\n", - "当训练作业提交到PAI执行时,需要按一定规范读取输入数据,以及将需要保存的模型写出到指定路径下,更加具体介绍请见文档:[提交训练作业](https://help.aliyun.com/document_detail/2261505.html)。\n", - "\n", - "修改后的训练作业拉起脚本如下:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "vscode": { - "languageId": "shellscript" - } - }, - "outputs": [], - "source": [ - "%%writefile ChatGLM2-6B/ptuning/train.sh\n", - "\n", - "PRE_SEQ_LEN=128\n", - "LR=2e-2\n", - "NUM_GPUS=`nvidia-smi --list-gpus | wc -l`\n", - "\n", - "torchrun --standalone --nnodes=1 --nproc-per-node=$NUM_GPUS main.py \\\n", - " --do_train \\\n", - " --train_file /ml/input/data/train/train.json \\\n", - " --validation_file /ml/input/data/train/dev.json \\\n", - " --preprocessing_num_workers 10 \\\n", - " --prompt_column content \\\n", - " --response_column summary \\\n", - " --overwrite_cache \\\n", - " --model_name_or_path /ml/input/data/model \\\n", - " --output_dir /ml/output/model/ \\\n", - " --overwrite_output_dir \\\n", - " --max_source_length 64 \\\n", - " --max_target_length 128 \\\n", - " --per_device_train_batch_size 4 \\\n", - " --per_device_eval_batch_size 4 \\\n", - " --gradient_accumulation_steps 32 \\\n", - " --predict_with_generate \\\n", - " --num_train_epochs 10 \\\n", - " --save_strategy epoch \\\n", - " --learning_rate $LR \\\n", - " --pre_seq_len $PRE_SEQ_LEN \\\n", - " --quantization_bit 4\n" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "这里我们将使用PAI提供的PyTorch GPU训练镜像运行训练作业,需要安装部分第三方依赖包。用户可以通过提供`requirements.txt`的方式提供,相应的依赖会在训练作业执行前被安装到环境中\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "vscode": { - "languageId": "shellscript" - } - }, - "outputs": [], - "source": [ - "%%writefile ChatGLM2-6B/ptuning/requirements.txt\n", - "# 模型需要的依赖\n", - "transformers==4.30.2\n", - "accelerate\n", - "icetk\n", - "cpm_kernels\n", - "\n", - "torch>=2.0,<2.1\n", - "sentencepiece\n", - "accelerate\n", - "\n", - "rouge_chinese\n", - "nltk\n", - "jieba\n", - "datasets" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 提交训练作业\n", - "\n", - "我们将通过PAI Python SDK,将以上的训练作业提交到PAI执行。SDK在提交训练作业之后,会打印训练作业的链接,用户可以通过对应的链接查看作业的执行详情,输出日志。\n", - "\n", - "> Note:按当前示例教程使用的训练配置、数据集和机器规格,训练作业运行约10分钟左右。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pai.estimator import Estimator\n", - "from pai.image import retrieve\n", - "\n", - "# 使用PAI提供的最新的PyTorch推理镜像\n", - "image_uri = retrieve(\n", - " \"PyTorch\",\n", - " \"latest\",\n", - " accelerator_type=\"GPU\",\n", - ").image_uri\n", - "\n", - "\n", - "est = Estimator(\n", - " command=\"bash train.sh\", # 启动命令\n", - " source_dir=\"./ChatGLM2-6B/ptuning\", # 训练代码目录.\n", - " image_uri=image_uri, # 训练镜像\n", - " instance_type=\"ecs.gn6e-c12g1.3xlarge\", # 使用的机器规格示例,V100(32G)\n", - " base_job_name=\"chatglm2_finetune_\",\n", - ")\n", - "\n", - "\n", - "# 提交训练作业\n", - "est.fit(\n", - " inputs={\n", - " \"model\": model_uri,\n", - " \"train\": train_data_uri,\n", - " }\n", - ")" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "默认`estimator.fit`会等待到作业执行完成。作业执行成功之后,用户可以通过`est.model_data()`获取输出模型在OSS上的路径地址。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(est.model_data())" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "用户可以通过`ossutil`或是SDK提供的便利方法将模型下载到本地:\n", - "\n", - "```python\n", - "from pai.common.oss_util import download\n", - "\n", - "\n", - "# 使用SDK的便利方法下载模型到本地.\n", - "download(\n", - "\toss_path=est.model_data(),\n", - "\tlocal_path=\"./output_model\",\n", - ")\n", - "\n", - "```" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### 部署微调之后的模型\n", - "\n", - "微调训练之后获得的`checkpoints`,需要和原始的模型配合一起使用。我们需要通过以下代码获得对应的checkpoint路径.\n", - "\n", - "> 用户通过修改微调训练的代码,使用`Trainer.save_model()`显式的保存相应的checkpoints,则可以直接通过`estimator.model_data()`下获得相应的checkpoints." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "\n", - "# 以上的训练作业超参设置中,我们设置`epochs=2`, checkpoints保存的策略是`每一个epochs保存`。\n", - "# 默认最后一个checkpoint会被保存到`{output_dir}/checkpoint-2`路径下.\n", - "# 通过以下路径,我们可以获得模型训练获得的最后一个checkpoint的OSS路径.\n", - "\n", - "checkpoint_uri = os.path.join(est.model_data(), \"checkpoint-10/\")\n", - "print(checkpoint_uri)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "我们将复用ChatGLM2部署的推理服务程序创建推理服务。与直接部署ChatGLM2的不同点在于我们还需要提供微调之后获得的checkpoints。\n", - "\n", - "通过`InferenceSpec.mount` API,我们可以将相应的OSS模型路径挂载到服务容器中,供推理服务程序使用。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "from pai.model import container_serving_spec, Model\n", - "from pai.image import retrieve, ImageScope\n", - "\n", - "\n", - "# InferenceSpec用于描述如何创建推理服务\n", - "infer_spec = container_serving_spec(\n", - " image_uri=retrieve( # 使用PAI提供的最新PyTorch的推理镜像\n", - " \"PyTorch\",\n", - " \"latest\",\n", - " accelerator_type=\"GPU\",\n", - " image_scope=ImageScope.INFERENCE,\n", - " ),\n", - " source_dir=\"./server_src\", # 代码目录\n", - " command=\"python run.py\", # 启动命令\n", - ")\n", - "\n", - "\n", - "# 将相应的checkpoints挂载到服务中,推理服务的程序通过检查目录(/ml/ptuning_checkpoints/)是否存在加载checkpoints\n", - "infer_spec.mount(checkpoint_uri, \"/ml/ptuning_checkpoints\")\n", - "print(infer_spec.to_dict())" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from pai.common.utils import random_str\n", - "\n", - "m = Model(\n", - " model_data=model_uri,\n", - " inference_spec=infer_spec,\n", - ")\n", - "\n", - "# 部署模型\n", - "p = m.deploy(\n", - " service_name=\"chatglm_ft_{}\".format(random_str(6)),\n", - " instance_type=\"ecs.gn6i-c16g1.4xlarge\", # 1 * T4\n", - " options={\n", - " # 配置EAS RPC框架的超时时间, 单位为毫秒\n", - " \"metadata.rpc.keepalive\": 20000,\n", - " },\n", - ")" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "向推理服务发送请求,测试推理服务是否正常启动。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "resp = p.raw_predict(\n", - " {\n", - " \"prompt\": \"你好\",\n", - " },\n", - ")\n", - "print(resp.json())" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "基于以上微调后模型的推理服务,我们可以使用Gradio创建一个新的机器人。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import gradio as gr\n", - "import random\n", - "import time\n", - "\n", - "with gr.Blocks() as demo:\n", - " chatbot = gr.Chatbot()\n", - " msg = gr.Textbox()\n", - " clear = gr.Button(\"Clear\")\n", - " submit = gr.Button(\"Submit\")\n", - "\n", - " def respond(message, chat_history):\n", - "\n", - " print(f\"Message: {message}\")\n", - " print(f\"ChatHistory: {chat_history}\")\n", - " resp = p.raw_predict(\n", - " {\n", - " \"prompt\": message,\n", - " \"history\": chat_history,\n", - " }\n", - " ).json()\n", - " print(f\"Response: {resp['response']}\")\n", - "\n", - " chat_history.append((message, resp[\"response\"]))\n", - " return \"\", chat_history\n", - "\n", - " submit.click(respond, [msg, chatbot], [msg, chatbot])\n", - "\n", - "demo.launch(share=True)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "在测试完成之后,可以通过`p.delete_service()`删除服务,释放资源。\n", - "\n", - "> 请注意,删除在线推理服务之后,对应的Gradio的应用将无法使用。" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "p.delete_service()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.16" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/source/tutorial/chatglm_finetune/resource/gradio-chatglml.jpg b/docs/source/tutorial/chatglm_finetune/resource/gradio-chatglml.jpg deleted file mode 100644 index 13848a559dc130dfc7ed6328666feac19f3f902c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 206542 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