# -*- coding: utf-8 -*-

import re
import time
import os
import json
import types
import datetime

from qcloud_cos import CosConfig
from qcloud_cos import CosS3Client
from qcloud_cos.cos_threadpool import SimpleThreadPool

from tikit.tencentcloud.common import credential
from tikit.tencentcloud.common.profile.client_profile import ClientProfile
from tikit.tencentcloud.common.profile.http_profile import HttpProfile
from tikit.tencentcloud.tione.v20211111 import tione_client
from tikit.tencentcloud.tione.v20211111 import models
from tikit.tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException
from tikit import models as TiModels

from .util import parse_cos_str

SCHEMA_INFO_VALUE_TYPES = ["TYPE_INT", "TYPE_STRING", "TYPE_BOOL", "TYPE_FLOAT"]
ANNOTATION_TYPES = ["ANNOTATION_TYPE_CLASSIFICATION",
                    "ANNOTATION_TYPE_DETECTION",
                    "ANNOTATION_TYPE_SEGMENTATION",
                    "ANNOTATION_TYPE_TRACKING",
                    "ANNOTATION_TYPE_OCR"]
ANNOTATION_FORMATS = ["ANNOTATION_FORMAT_TI",
                      "ANNOTATION_FORMAT_PASCAL",
                      "ANNOTATION_FORMAT_COCO",
                      "ANNOTATION_FORMAT_FILE"]
ALL_SUPPORT_TRAINING_MODES = ["PS_WORKER", "DDP", "MPI", "HOROVOD"]


class Client(object):
    # 临时秘钥使用，提前获取临时秘钥，避免访问的时候失败
    _secret_ahead_time = 60 * 5

    def __init__(self, secret_id=None, secret_key=None, region=None, token=None, proxies=None, tione_endpoint=None):
        """初始化，保存用户的信息

        :param secret_id:   秘钥SecretId
        :type secret_id:    str
        :param secret_key:  秘钥SecretKey
        :type secret_key:   str
        :param region:      地域信息
        :type region:       str
        :param token:       临时秘钥使用的token
        :type token:        str
        :param proxies:     使用代理来访问，内容如：{"https": "127.0.0.1:443"}.
        :type proxies:      dict
        """
        self._secret_id = secret_id
        self._secret_key = secret_key
        self._region = region if region is not None else os.getenv("REGION")
        self._token = token
        self._proxies = proxies

        self._cos_client = None
        self._tione_client = None
        # self._tione_client = tione_client.TioneClient(credential.Credential(secret_id, secret_key, token), self._region)

        self._expired_time = 0
        default_tione_enpoint = "tione.tencentcloudapi.com"
        if os.getenv("KUBERNETES_SERVICE_HOST") is not None and os.getenv("TI_INSTANCE_ID") is not None \
                and os.getenv("TI_TASK_ID") is not None:
            default_tione_enpoint = "tione.internal.tencentcloudapi.com"
        self._tione_endpoint = tione_endpoint if tione_endpoint is not None else default_tione_enpoint

        if secret_id is not None:
            self._init_client()

    def _init_client(self):
        config = CosConfig(Region=self._region, SecretId=self._secret_id, SecretKey=self._secret_key,
                           Token=self._token, Proxies=self._proxies)
        self._cos_client = CosS3Client(config)

        cred = credential.Credential(self._secret_id, self._secret_key, self._token)
        proxy = None if self._proxies is None else self._proxies.get("https", None)
        http_profile = HttpProfile(endpoint=self._tione_endpoint, proxy=proxy, keepAlive=True)
        client_profile = ClientProfile(httpProfile=http_profile)
        self._tione_client = tione_client.TioneClient(cred, self._region, client_profile)

    def _update_secret(self):
        # self._region = os.getenv("REGION")
        # # TODO 从环境变量的认证URL中获取临时认证信息
        # self._secret_id = os.getenv("SECRET_ID")
        # self._secret_key = os.getenv("SECRET_KEY")
        # self._token =
        # self._expired_time =
        raise Exception("auto authentication is not supported")

    def guarantee_valid(self):
        if self._expired_time - self._secret_ahead_time < int(time.time()):
            self._update_secret()
            self._init_client()

    def describe_cos_buckets(self):
        """列出所有bucket

        :return:    bucket列表
        :rtype:     dict
        返回的结果如：
        {
          "Owner": {
            "ID": "qcs::cam::uin/100011011262:uin/100011011262",
            "DisplayName": "100011011162"
          },
          "Buckets": {
            "Bucket": [
              {
                "Name": "bucket-58565",
                "Location": "ap-beijing-fsi",
                "CreationDate": "2021-07-21T11:06:00Z",
                "BucketType": "cos"
              },
              {
                "Name": "tai-1300158565",
                "Location": "ap-guangzhou",
                "CreationDate": "2021-10-22T11:04:40Z",
                "BucketType": "cos"
              }
            ]
          }
        }
        """
        return self._cos_client.list_buckets()

    def is_cos_dir(self, bucket, path):
        """判断目录是否为文件夹

        :param bucket:
        :type bucket:
        :param path:
        :type path:
        :return:
        :rtype:
        """
        objects = self._cos_client.list_objects(
            Bucket=bucket,
            Prefix=path.strip("/") + "/",
            MaxKeys=1
        )
        if "Contents" in objects and len(objects["Contents"]) > 0:
            return True
        return False

    def check_object_exist(self, bucket, cos_path):
        """ 判断某个object是否存在
        如果是判断某个目录存在，需要保证cos_path 以 / 结尾
        如果是判断某个文件存在，需要保证cos_path 不以 / 结尾

        :param bucket:      cos上的桶
        :param cos_path:    cos上的路径key
        :return:            指定对象是否存在
        :rtype:             bool, 错误信息通过 raise 给出
        """
        rsp = self._cos_client.object_exists(bucket, cos_path)
        return rsp

    def mkdir(self, bucket, cos_path):
        """ cos上创建目录
        注: 对象存储本身是没有目录概念, 为了保证用户的使用习惯, 此处创建一个以cos_path为指定key, key以 / 结尾的0kb大小的对象

        :param bucket:      cos上的桶
        :param cos_path:    cos上的路径key
        :return:            None, 错误信息通过 raise 给出
        """
        cos_path = str(cos_path)
        if cos_path.endswith("/"):
            cos_path = cos_path.rstrip("/")
        cos_path = cos_path + "/"
        self._cos_client.put_object(Bucket=bucket, Key=cos_path, Body=b'')

    def upload_to_cos(self, local_path, bucket, cos_path):
        """把本地路径下的文件或者目录上传到cos上

        :param local_path:  本地路径
        :type local_path:   str
        :param bucket:      cos上的桶
        :type bucket:       str
        :param cos_path:    cos上的路径
        :type cos_path:     str
        :return:            None. 错误信息通过 raise 给出。
        :rtype:
        """
        if not os.path.exists(local_path):
            raise Exception("local path is not exist, please check it.")

        # 不管cos_path是否存在, 都以此路径创建目录
        self.mkdir(bucket, cos_path)

        if os.path.isfile(local_path):
            if cos_path.endswith("/"):
                cos_path = os.path.join(cos_path, os.path.basename(local_path))
            self._cos_client.upload_file(
                Bucket=bucket,
                LocalFilePath=local_path,
                Key=cos_path,
                EnableMD5=True
            )
            return

        local_path_length = len(local_path)
        if os.path.isdir(local_path):
            walk = os.walk(local_path)
            # 创建上传的线程池
            pool = SimpleThreadPool()
            base_path = cos_path
            if self.is_cos_dir(bucket, cos_path):
                base_path = os.path.join(base_path, os.path.basename(local_path))
            for path, dir_list, file_list in walk:
                pre_path = os.path.join(base_path, path[local_path_length:].strip('/'))
                for file_name in file_list:
                    src_key = os.path.join(path, file_name)
                    cos_object_key = os.path.join(pre_path, file_name).strip('/')
                    # 假如存在错误的软链时, 或者其他意外情况导致的文件不存在时, 跳过
                    if not os.path.exists(src_key):
                        print(src_key, 'does not exist, ignore it')
                        continue
                    pool.add_task(self._cos_client.upload_file, bucket, cos_object_key, src_key)

            pool.wait_completion()
            result = pool.get_result()
            if not result['success_all']:
                self.__dump_error_result(result)
                raise Exception("failed to upload files to cos, please retry")
            return

    def describe_cos_path(self, bucket, path, maker="", max_keys=1000, encoding_type=""):
        """获取cos的目录的信息。目录下的内容最多显示1000个。要显示目录下的文件和文件夹，请以"/"结尾

        :param bucket:          cos的桶
        :type bucket:           str
        :param path:            路径
        :type path:             str
        :return:                目录信息
        :rtype:                 dict
        :param maker:           从marker开始列出条目
        :type maker:            str
        :param max_keys:        设置单次返回最大的数量,最大为1000.
        :type max_keys:         int
        :param encoding_type:   设置返回结果编码方式,只能设置为url.
        :type encoding_type:    str
        """
        return self._cos_client.list_objects(
            Bucket=bucket,
            Prefix=path,
            Delimiter="/",
            Marker=maker,
            MaxKeys=max_keys,
            EncodingType=encoding_type
        )

    def delete_cos_files(self, bucket, file_infos):
        """删除cos的一个或者多个文件

        :param bucket:
        :type bucket:
        :param file_infos:
        :type file_infos:
        :return:
        :rtype:
        """
        delete_list = []
        for file in file_infos:
            delete_list.append({"Key": file['Key']})

        response = self._cos_client.delete_objects(Bucket=bucket, Delete={"Object": delete_list})
        print(response)

    def delete_cos_path(self, bucket, delete_path):
        """删除cos目录。要删除目录下的文件和文件夹，请以"/"结尾，即：不带斜杠的，当成文件来删除；带斜杠的，当成文件夹来删除

        :param bucket:      cos的桶
        :type bucket:       str
        :param delete_path: 要删除的路径
        :type delete_path:  str
        """
        if not delete_path.endswith("/"):
            return self._cos_client.delete_object(
                Bucket=bucket,
                Key=delete_path,
            )
            return

        pool = SimpleThreadPool()
        marker = ""
        while True:
            file_infos = []
            response = self._cos_client.list_objects(Bucket=bucket, Prefix=delete_path, Marker=marker, MaxKeys=100)
            if "Contents" in response:
                contents = response.get("Contents")
                file_infos.extend(contents)
                pool.add_task(self.delete_cos_files, bucket, file_infos)
            # 列举完成，退出
            if response['IsTruncated'] == 'false':
                break

            # 列举下一页
            marker = response["NextMarker"]

        pool.wait_completion()
        result = pool.get_result()
        if not result['success_all']:
            raise Exception("failed to delete files, please retry")

    def __dump_error_result(self, result):
        """
        私有方法, 用来dump出cos的SimpleThreadPool运行过程中的错误信息
        """
        if 'detail' not in result:
            return

        detail = result['detail']
        for tp in detail:
            if tp[1] != 0:
                # 当failed的任务个数不为0时，打印第三个返回值, 即错误信息
                print(tp[2])

    def download_from_cos(self, bucket, cos_path, local_path):
        """从cos上下载文件或者目录本地。
        注意：本地文件存在会直接覆盖。 cos_path为目录且local_path 为存在的目录的时候，cos_path的文件夹名会作为子目录保留。

        :param bucket:      cos上的桶
        :type bucket:       str
        :param cos_path:    cos上的路径
        :type cos_path:     str
        :param local_path:  本地路径, 需要为目录
        :type local_path:   str
        :return:            None. 错误信息通过 raise 给出。
        :rtype:
        """
        file_infos = self.get_cos_sub_files(bucket, cos_path)
        if len(file_infos) == 0:
            raise Exception("cos path \"%s\" not existed" % cos_path)
        if os.path.isfile(local_path):
            raise Exception("local_path parameter \"%s\" should be directory" % local_path)

        # 下载目录时，如果不存在则创建
        os.makedirs(local_path, exist_ok=True)
        target_path = local_path
        # local_path 为存在的目录的时候
        if os.path.isdir(local_path):
            target_path = os.path.join(target_path, os.path.basename(cos_path))
        cos_path_length = len(cos_path)
        pool = SimpleThreadPool()
        for file in file_infos:
            # 文件下载 获取文件到本地
            file_cos_key = file["Key"]
            local_name = os.path.join(target_path, file_cos_key[cos_path_length:].strip('/')).strip("/")

            # 如果存在目录类型的object, 则直接跳过不做下载操作
            file_cos_key = str(file_cos_key)
            if file_cos_key.endswith("/") and int(file["Size"]) == 0:
                continue

            # 如果本地目录结构不存在，递归创建
            local_dir = os.path.dirname(local_name)
            if local_dir != "" and not os.path.exists(local_dir):
                os.makedirs(local_dir)

            # 使用线程池方式
            if self.__need_download_from_cos(bucket, file_cos_key, local_name):
                pool.add_task(self._cos_client.download_file, bucket, file_cos_key, local_name)

        pool.wait_completion()
        result = pool.get_result()
        if not result['success_all']:
            self.__dump_error_result(result)
            raise Exception("failed to download files, please retry")

    def get_cos_sub_files(self, bucket, prefix):
        """列出当前目录子节点，返回所有子节点信息

        :param bucket:
        :type bucket:
        :param prefix:
        :type prefix:
        :return:
        :rtype:
        """
        file_infos = []
        marker = ""
        while True:
            response = self._cos_client.list_objects(bucket, prefix, "", marker)
            if "Contents" in response:
                contents = response.get("Contents")
                file_infos.extend(contents)

            if "NextMarker" in response.keys():
                marker = response["NextMarker"]
            else:
                break

        sorted(file_infos, key=lambda file_info: file_info["Key"])
        # for file in file_infos:
        #     print(file)
        return file_infos

    def __need_download_from_cos(self, bucket, key, local_path):
        """判断当前文件是否需要下载

        :param bucket:              cos bucket
        :type bucket:               str
        :param key:                 cos file key
        :type key:                  str
        :param local_file_path:     local file path
        :type local_file_path:      str
        :return:                    是否需要下载当前文件
        :rtype:                     bool
        """
        # 如果最后有'/'，则移除
        local_path = os.path.normpath(local_path)
        if not os.path.exists(local_path):
            # 本地不存在，则需要从cos上下载
            return True

        local_file_size = os.path.getsize(local_path)
        try:
            response = self._cos_client.head_object(Bucket=bucket, Key=key)
            if 'Content-Length' not in response:
                # 异常情况, 重新下载
                print("Content-Length field is not exist, cos file key:", key)
                return True

            # 根据文件大小是否相同决定是否下载
            return int(response['Content-Length']) != int(local_file_size)
        except TencentCloudSDKException as err:
            raise

    def push_training_metrics(self, timestamp, value_map, task_id=None, epoch=None, total_steps=None, step=None):
        """上报训练自定义指标（单条）。单个子账号每秒可以调用20次，请在您的训练代码中注意控制上报频率，避免超限报错。或者使用push_training_metrics_list

        :param timestamp:   时间戳
        :type timestamp:    int
        :param value_map:   指标映射。 指标名称 -> 指标值
        :type value_map:    map: str -> float
        :param task_id:     任务ID。若为空，就当前取任务节点环境的 TI_TASK_ID 环境变量的值
        :type task_id:      str
        :param epoch:       epoch值
        :type epoch:        int
        :param total_steps: 总步数
        :type total_steps:  int
        :param step:        第几步
        :type step:         int
        :return:
        :rtype:             :class:`tikit.tencentcloud.tione.v20211111.models.PushTrainingMetricsResponse`

        client.push_training_metrics(int(time.time()), {"field1": 11, "field2": 12}, "task-id-00001", 3, 1000, 66)
        """
        try:
            metric = models.MetricData()
            if task_id:
                metric.TaskId = task_id
            else:
                metric.TaskId = os.getenv("TI_TASK_ID")
            if not metric.TaskId or len(metric.TaskId) == 0:
                raise TencentCloudSDKException(message="task id cannot be empty")

            metric.Timestamp = timestamp
            metric.Epoch = epoch
            metric.TotalSteps = total_steps
            metric.Step = step
            metric.Points = []
            for pair in value_map.items():
                metric.Points.append({"Name": pair[0], "Value": pair[1]})
            metric_list = [metric]
            req = models.PushTrainingMetricsRequest()
            req.Data = metric_list

            return self._tione_client.PushTrainingMetrics(req)

        except TencentCloudSDKException as err:
            raise

    def push_training_metrics_list(self, metric_list):
        """上报训练自定义指标（列表）

        :param metric_list: MetricData 数组。 若任务ID为空，就当前取任务节点环境的 TI_TASK_ID 环境变量的值
        :type metric_list:  list of :class:`tikit.tencentcloud.tione.v20211111.models.MetricData`
        :return:
        :rtype:             :class:`tikit.tencentcloud.tione.v20211111.models.PushTrainingMetricsResponse`
        """
        try:
            for i in range(len(metric_list)):
                if not metric_list[i].TaskId or len(metric_list[i].TaskId) == 0:
                    metric_list[i].TaskId = os.getenv("TI_TASK_ID")
                if not metric_list[i].TaskId or len(metric_list[i].TaskId) == 0:
                    raise TencentCloudSDKException(message="task id cannot be empty")

                if metric_list[i].Timestamp is None:
                    raise Exception("field Timestamp cannot be empty")

            req = models.PushTrainingMetricsRequest()
            req.Data = metric_list

            return self._tione_client.PushTrainingMetrics(req)

        except TencentCloudSDKException as err:
            raise

    def describe_training_metrics(self, task_id):
        """查询训练自定义指标

        :param task_id: 任务ID
        :type task_id: str
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.DescribeTrainingMetricsResponse`
        """
        try:
            req = models.DescribeTrainingMetricsRequest()
            req.TaskId = task_id
            return self._tione_client.DescribeTrainingMetrics(req)
        except TencentCloudSDKException as err:
            raise

    def describe_train_resource_groups(self, offset=0, limit=20, search_word="", tag_filters=None):
        """获取训练资源组列表

        :param offset: 偏移量，默认为0；分页查询起始位置，如：Limit为100，第一页Offset为0，第二页OffSet为100....即每页左边为开区间
        :type offset: int
        :param limit: 返回数量，默认为20，最大值为30；分页查询每页大小，最大30
        :type limit: int
        :param search_word: 支持模糊查找资源组id和资源组名
        :type search_word: str
        :param tag_filters: 标签过滤
        :type tag_filters: list of tikit.tencentcloud.tione.v20211111.models.Tag
        :return:
        :rtype:    :class:`tikit.tencentcloud.tione.v20211111.models.DescribeBillingResourceGroupsResponse`
        """
        try:
            req = models.DescribeBillingResourceGroupsRequest()
            req.Type = "TRAIN"
            req.Offset = offset
            req.Limit = limit
            req.SearchWord = search_word
            req.TagFilters = tag_filters
            return self._tione_client.DescribeBillingResourceGroups(req)
        except TencentCloudSDKException as err:
            raise

    def describe_inference_resource_groups(self, offset=0, limit=20, search_word="", tag_filters=None):
        """获取推理训练组列表

        :param offset: 偏移量，默认为0；分页查询起始位置，如：Limit为100，第一页Offset为0，第二页OffSet为100....即每页左边为开区间
        :type offset: int
        :param limit: 返回数量，默认为20，最大值为30；分页查询每页大小，最大30
        :type limit: int
        :param search_word: 支持模糊查找资源组id和资源组名
        :type search_word: str
        :param tag_filters: 标签过滤
        :type tag_filters: list of tikit.tencentcloud.tione.v20211111.models.Tag
        :return:
        :rtype:    :class:`tikit.tencentcloud.tione.v20211111.models.DescribeBillingResourceGroupsResponse`
        """
        try:
            req = models.DescribeBillingResourceGroupsRequest()
            req.Type = "INFERENCE"
            req.Offset = offset
            req.Limit = limit
            req.SearchWord = search_word
            req.TagFilters = tag_filters
            return self._tione_client.DescribeBillingResourceGroups(req)
        except TencentCloudSDKException as err:
            raise

    def describe_postpaid_training_price(self):
        """查询每种配置的每小时的价格，价格单位：元

        :rtype:     tikit.tencentcloud.tione.v20211111.models.DescribeBillingSpecsResponse
        """
        try:
            req = models.DescribeBillingSpecsRequest()
            req.TaskType = "TRAIN"
            req.ChargeType = "POSTPAID_BY_HOUR"
            specs = self._tione_client.DescribeBillingSpecs(req)

            price_req = models.DescribeBillingSpecsPriceRequest()
            price_req.SpecsParam = []
            for spec in specs.Specs:
                price_req.SpecsParam.append({"SpecName": spec.SpecName, "SpecCount": 1})
            price_result = self._tione_client.DescribeBillingSpecsPrice(price_req)
            for i in range(len(price_result.SpecsPrice)):
                specs.Specs[i].SpecId = str(price_result.SpecsPrice[i].RealTotalCost / 100.0)
            return specs
        except TencentCloudSDKException as err:
            raise
    
    def describe_postpaid_reasoning_price(self):
        """查询每种配置的每小时的价格，价格单位：元

        :rtype:     tikit.tencentcloud.tione.v20211111.models.DescribeBillingSpecsResponse
        """
        try:
            req = models.DescribeBillingSpecsRequest()
            req.TaskType = "INFERENCE"
            req.ChargeType = "POSTPAID_BY_HOUR"
            specs = self._tione_client.DescribeBillingSpecs(req)

            price_req = models.DescribeBillingSpecsPriceRequest()
            price_req.SpecsParam = []
            for spec in specs.Specs:
                price_req.SpecsParam.append({"SpecName": spec.SpecName, "SpecCount": 1})
            price_result = self._tione_client.DescribeBillingSpecsPrice(price_req)
            for i in range(len(price_result.SpecsPrice)):
                specs.Specs[i].SpecId = str(price_result.SpecsPrice[i].RealTotalCost / 100.0)
            return specs
        except TencentCloudSDKException as err:
            raise

    def describe_training_tasks(self, filters=None, tag_filters=None, offset=0, limit=50, order="DESC",
                                order_field="UpdateTime"):
        """获取训练任务列表

        :param filters:     过滤器，eg：[{ "Name": "TaskStatus", "Values": ["Running"] }]
        :type filters:      list of Filter
        :param tag_filters: 标签过滤器，eg：[{ "TagKey": "TagKeyA", "TagValue": ["TagValueA"] }]
        :type tag_filters:  list of TagFilter
        :param offset:      偏移量，默认为0
        :type offset:       int
        :param limit:       返回数量，默认为50
        :type limit:        int
        :param order:       输出列表的排列顺序。取值范围：ASC：升序排列 DESC：降序排列
        :type order:        str
        :param order_field: 排序的依据字段， 取值范围 "CreateTime" "UpdateTime"
        :type order_field:  str
        :return:
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.DescribeTrainingTasksResponse`
        """
        try:
            # 优化参数的显示
            req = models.DescribeTrainingTasksRequest()
            req.Filters = filters
            req.TagFilters = tag_filters
            req.Offset = offset
            req.Limit = limit
            req.Order = order
            req.OrderField = order_field
            return self._tione_client.DescribeTrainingTasks(req)
        except TencentCloudSDKException as err:
            raise

    def describe_training_task(self, task_id):
        """获取单个训练任务信息

        :param task_id: 训练任务ID
        :type task_id: str
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.DescribeTrainingTaskResponse`
        """
        try:
            # 优化参数的显示
            req = models.DescribeTrainingTaskRequest()
            req.Id = task_id
            # TODO 优化显示结果
            return self._tione_client.DescribeTrainingTask(req)
        except TencentCloudSDKException as err:
            raise

    def parse_cos_info(self, cos_str):
        """ 解析cos 字符串成结构体

        :param cos_str: 格式如： <bucket>/<cos path>
        :type cos_str: str
        :return:
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.CosPathInfo`
        """
        cos_info = parse_cos_str(cos_str)
        cos_info.Region = self._region
        return cos_info

    def create_training_task(self,
                             name,
                             framework,
                             cos_output,
                             worker_resource,
                             code_package_path,
                             ps_resource=None,
                             input_data_config=None,
                             worker_start_cmd=None,
                             ps_start_cmd=None,
                             tags=None,
                             tuning_parameters_dict={},
                             resource_group_id="",
                             remark=None,
                             log_enable=False,
                             log_logset_id=None,
                             log_topic_id=None,
                             vpc_id=None,
                             sub_net_id=None,
                             enable_rdma=False):
        """创建训练任务

        :param name:        任务名称
        :type name:         str
        :param framework:   运行的框架环境
        :type framework:    :class:`tikit.models.FrameworkInfo`
        :param cos_output:          输出的cos信息
        :type cos_output:           str
        :param worker_resource:     worker节点的配置
        :type worker_resource:      :class:`tikit.models.ResourceConfigInfo`
        :param code_package_path:   代码的cos信息
        :type code_package_path:    str
        :param ps_resource:         ps节点的配置
        :type ps_resource:          :class:`tikit.models.ResourceConfigInfo`
        :param input_data_config:   输入的数据信息
        :type input_data_config:    list or :class:`tikit.models.TrainingDataConfig`
        :param worker_start_cmd:    worker的启动命令
        :type worker_start_cmd:     str
        :param ps_start_cmd:        ps节点的启动命令
        :type ps_start_cmd:         str
        :param tags:                标签
        :type tags:                 list of :class:`tikit.tencentcloud.tione.v20211111.models.Tag`
        :param tuning_parameters_dict:  调参字典
        :type tuning_parameters_dict:   dict
        :param resource_group_id:   预付费的资源组id
        :type resource_group_id:    str
        :param remark:              描述
        :type remark:               str
        :param log_enable:          日志开关
        :type log_enable:           bool
        :param log_logset_id:       日志集id
        :type log_logset_id:        str
        :param log_topic_id:        日志的topic id
        :type log_topic_id:         str
        :param vpc_id:              vpc的id
        :type vpc_id:               str
        :param sub_net_id:          子网id
        :type sub_net_id:           str
        :param enable_rdma:         是否使用 RMDA 网卡，只有机型支持该参数才可生效
        :type enable_rdma:          bool
        :return:
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.CreateTrainingTaskResponse`
        """
        try:
            req = models.CreateTrainingTaskRequest()
            req.Name = name

            req.TrainingMode = framework.TrainingMode
            if framework.Name == "CUSTOM":
                req.ImageInfo = models.ImageInfo()
                req.ImageInfo.ImageType = framework.ImageType
                req.ImageInfo.ImageUrl = framework.ImageUrl
                req.ImageInfo.RegistryRegion = framework.RegistryRegion
                req.ImageInfo.RegistryId = framework.RegistryId
            else:
                req.FrameworkName = framework.Name
                req.FrameworkEnvironment = framework.FrameworkEnvironment

            if req.TrainingMode not in ALL_SUPPORT_TRAINING_MODES:
                raise TencentCloudSDKException(
                    message='only support these training modes {}'.format(ALL_SUPPORT_TRAINING_MODES))
            if req.TrainingMode == "PS_WORKER":
                if not ps_resource:
                    raise TencentCloudSDKException(message='PS_WORKER training mode, need the argument "ps_resource"')
                if worker_resource.ChargeType != ps_resource.ChargeType:
                    raise TencentCloudSDKException(
                        message='worker_resource charge type should be same with ps_resource')
            elif ps_resource:
                raise TencentCloudSDKException(message='only PS_WORKER training mode,need the argument "ps_resource"')

            req.ChargeType = worker_resource.ChargeType
            req.ResourceGroupId = resource_group_id

            worker_info = models.ResourceConfigInfo()
            worker_info.Role = "WORKER"
            worker_info.InstanceNum = worker_resource.InstanceNum
            worker_info.Cpu = worker_resource.Cpu
            worker_info.Memory = worker_resource.Memory
            worker_info.GpuType = worker_resource.GpuType
            worker_info.Gpu = worker_resource.Gpu
            worker_info.InstanceType = worker_resource.InstanceType
            if enable_rdma:
                rdma_config = models.RDMAConfig()
                rdma_config.Enable = enable_rdma
                worker_info.RDMAConfig = rdma_config
            req.ResourceConfigInfos = [worker_info]

            if ps_resource:
                ps_info = models.ResourceConfigInfo()
                ps_info.Role = "PS"
                ps_info.InstanceNum = ps_resource.InstanceNum
                ps_info.Cpu = ps_resource.Cpu
                ps_info.Memory = ps_resource.Memory
                ps_info.GpuType = ps_resource.GpuType
                ps_info.Gpu = ps_resource.Gpu
                ps_info.InstanceType = ps_resource.InstanceType
                if enable_rdma:
                    rdma_config = models.RDMAConfig()
                    rdma_config.Enable = enable_rdma
                    ps_info.RDMAConfig = rdma_config
                req.ResourceConfigInfos.append(ps_info)

            req.Output = self.parse_cos_info(cos_output)
            req.Tags = tags
            req.CodePackagePath = self.parse_cos_info(code_package_path) if code_package_path else None
            req.StartCmdInfo = models.StartCmdInfo()
            req.StartCmdInfo.WorkerStartCmd = worker_start_cmd
            req.StartCmdInfo.PsStartCmd = ps_start_cmd

            if input_data_config is not None:
                req.DataConfigs, req.DataSource = self._parse_training_task_input_data(input_data_config)

            req.TuningParameters = json.dumps(tuning_parameters_dict)
            req.Remark = remark
            req.LogEnable = log_enable
            if log_enable:
                req.LogConfig = models.LogConfig()
                req.LogConfig.LogsetId = log_logset_id
                req.LogConfig.TopicId = log_topic_id

            req.VpcId = vpc_id
            req.SubnetId = sub_net_id

            return self._tione_client.CreateTrainingTask(req)
        except TencentCloudSDKException as err:
            raise

    def _parse_training_task_input_data(self, input_data_config):
        data_configs = []
        data_type = ""  # 兼容旧版本
        if not isinstance(input_data_config, list):
            # 兼容旧版本的处理
            if input_data_config.DataSource == "DATASET":
                for dataset_id in input_data_config.DataConfigDict:
                    data_config = models.DataConfig()
                    data_config.DataSourceType = input_data_config.DataSource
                    data_config.MappingPath = input_data_config.DataConfigDict[dataset_id]
                    data_config.DataSetSource = models.DataSetConfig()
                    data_config.DataSetSource.Id = dataset_id
                    data_configs.append(data_config)
            if input_data_config.DataSource == "COS":
                for cos_str in input_data_config.DataConfigDict:
                    data_config = models.DataConfig()
                    data_config.DataSourceType = input_data_config.DataSource
                    data_config.MappingPath = input_data_config.DataConfigDict[cos_str]
                    data_config.COSSource = self.parse_cos_info(cos_str)
                    data_configs.append(data_config)
            if input_data_config.DataSource == "GooseFS":
                data_config = models.DataConfig()
                data_config.DataSourceType = input_data_config.DataSource
                data_config.MappingPath = input_data_config.TargetPath
                data_config.GooseFSSource = models.GooseFSSource()
                data_config.GooseFSSource.Id = input_data_config.GooseFSId
                data_configs.append(data_config)
            if input_data_config.DataSource == "CFSTurbo":
                data_config = models.DataConfig()
                data_config.DataSourceType = input_data_config.DataSource
                data_config.MappingPath = input_data_config.TargetPath
                data_config.CFSTurboSource = models.CFSTurboSource()
                data_config.CFSTurboSource.Id = input_data_config.CFSTurboId
                data_configs.append(data_config)
            return data_configs, input_data_config.DataSource

        for input_data_item in input_data_config:
            data_config = models.DataConfig()
            data_config.DataSourceType = input_data_item.DataSource
            data_config.MappingPath = input_data_item.TargetPath
            if input_data_item.DataSource == "COS":
                data_config.COSSource = self.parse_cos_info(input_data_item.CosStr)
            elif input_data_item.DataSource == "DATASET":
                data_config.DataSetSource = models.DataSetConfig()
                data_config.DataSetSource.Id = input_data_item.DatasetId
            elif input_data_item.DataSource == "CFS":
                data_config.CFSSource = models.CFSConfig()
                data_config.CFSSource.Id = input_data_item.CfsId
                data_config.CFSSource.Path = input_data_item.CfsPath
            elif input_data_item.DataSource == "HDFS":
                data_config.HDFSSource = models.HDFSConfig()
                data_config.HDFSSource.Id = input_data_item.HdfsId
                data_config.HDFSSource.Path = input_data_item.HdfsPath
            elif input_data_item.DataSource == "WEDATA_HDFS":
                data_config.WeDataHDFSSource = models.WeDataHDFSConfig()
                data_config.WeDataHDFSSource.Id = input_data_item.WedataId
                data_config.WeDataHDFSSource.Path = input_data_item.HdfsPath
            elif input_data_item.DataSource == "AIMarket_Algo_PreModel":
                data_config.AIMarketAlgoPreModelSource = models.AIMarketAlgoPreModelSource()
                data_config.AIMarketAlgoPreModelSource.Id = input_data_item.AIMarketAlgoId
            elif input_data_item.DataSource == "GooseFS":
                data_config.GooseFSSource = models.GooseFSSource()
                data_config.GooseFSSource.Id = input_data_item.GooseFSId
            elif input_data_item.DataSource == "CFSTurbo":
                data_config.CFSTurboSource = models.CFSTurboSource()
                data_config.CFSTurboSource.Id = input_data_item.CFSTurboId
                data_config.CFSTurboSource.Path = input_data_item.CFSTurboPath
                
            data_configs.append(data_config)
            data_type = input_data_item.DataSource
            if data_type == "AIMarket_Algo_PreModel":
                data_type = "COS"
        return data_configs, data_type

    def stop_training_task(self, task_id):
        """停止某个训练任务

        :param task_id: 训练任务ID
        :type task_id: str
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.StopTrainingTaskResponse`
        """
        try:
            req = models.StopTrainingTaskRequest()
            req.Id = task_id
            return self._tione_client.StopTrainingTask(req)
        except TencentCloudSDKException as err:
            raise

    def describe_training_frameworks(self):
        """查看训练框架

        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.DescribeTrainingFrameworksResponse`
        print返回的结果，输出如下：
        +------------+----------------------------------------+-------------------------+
        | 框架名称   | 版本                                   | 训练模式                |
        +------------+----------------------------------------+-------------------------+
        | TENSORFLOW | ti-acc1.0-tf1.15-py3.6-cuda10.0-gpu    | PS_WORKER               |
        | TENSORFLOW | light3.1.3-tf2.4-py3.8-cuda11.1-gpu    | MPI, HOROVOD            |
        | TENSORFLOW | tf1.15-py3.7-cpu                       | PS_WORKER, MPI, HOROVOD |
        | TENSORFLOW | tf1.15-py3.7-cuda10.0-gpu              | PS_WORKER, MPI, HOROVOD |
        | TENSORFLOW | tf2.4-py3.8-cpu                        | PS_WORKER, MPI, HOROVOD |
        | TENSORFLOW | tf2.4-py3.8-cuda11.1-gpu               | PS_WORKER, MPI, HOROVOD |
        | PYTORCH    | ti-acc1.0-torch1.9-py3.8-cuda11.1-gpu  | DDP                     |
        | PYTORCH    | light3.1.3-torch1.9-py3.8-cuda11.1-gpu | DDP, MPI, HOROVOD       |
        | PYTORCH    | torch1.9-py3.8-cuda11.1-gpu            | DDP, MPI, HOROVOD       |
        | SPARK      | spark2.4.5-cpu                         | SPARK                   |
        | PYSPARK    | spark2.4.5-py3.6-cpu                   | SPARK                   |
        +------------+----------------------------------------+-------------------------+
        """
        try:
            req = models.DescribeTrainingFrameworksRequest()
            return self._tione_client.DescribeTrainingFrameworks(req)
        except TencentCloudSDKException as err:
            raise

    def delete_training_task(self, task_id):
        """删除某个训练任务

        :param task_id: 训练任务ID
        :type task_id: str
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.DeleteTrainingTaskResponse`
        """
        try:
            req = models.DeleteTrainingTaskRequest()
            req.Id = task_id
            return self._tione_client.DeleteTrainingTask(req)
        except TencentCloudSDKException as err:
            raise

    def describe_training_task_pods(self, task_id):
        """获取训练任务的pod列表

        :param task_id: 训练任务ID
        :type task_id: str
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.DescribeTrainingTaskPodsResponse`
        """
        try:
            req = models.DescribeTrainingTaskPodsRequest()
            req.Id = task_id
            return self._tione_client.DescribeTrainingTaskPods(req)
        except TencentCloudSDKException as err:
            raise

    def _describe_logs(self, service, pod_name, start_time=None, end_time=None, limit=None, order=None,
                       context=None, filters=None):
        """查看pod的日志

        :param service: 查询哪种类型的日志。 TRAIN：训练任务； NOTEBOOK：Notebook服务； INFER：推理服务；
        :type service: str
        """
        try:
            req = models.DescribeLogsRequest()
            req.Service = service
            req.PodName = pod_name
            req.StartTime = start_time
            req.EndTime = end_time
            req.Limit = limit
            req.Order = order
            req.Context = context
            req.Filters = filters
            return self._tione_client.DescribeLogs(req)
        except TencentCloudSDKException as err:
            raise

    def describe_train_logs(self, pod_name, start_time=None, end_time=None, limit=None, order=None,
                            context=None, filters=None):
        """查看训练任务的日志

        :param pod_name: 查询哪个Pod的日志，支持通配符。查看某个训练任务的全部pod的日志可以填： "<task_id>*"，如：train-51cd6bf7ec1000*
        :type pod_name: str
        :param start_time: 日志查询开始时间。RFC3339格式的时间字符串，比如2021-12-16T13:20:24+08:00，默认值为当前时间的前一个小时
        :type start_time: str
        :param end_time: 日志查询结束时间。RFC3339格式的时间字符串，比如2021-12-16T13:20:24+08:00，默认值为当前时间
        :type end_time: str
        :param limit: 日志查询条数，默认值100，最大值100
        :type limit: int
        :param order: 排序方向。(ASC | DESC) 默认值为DESC
        :type order: str
        :param context: 分页的游标
        :type context: str
        :param filters: 过滤Filters
        :type filters: list of tikit.tencentcloud.tione.v20211111.models.Filter
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.DescribeLogsResponse`

            返回的对象如果非空，就会有 next() 方法，能不断地获取下一页的日志（如果有多页的话），如下：
            now_time = datetime.datetime.now(datetime.timezone.utc)
            now_time_str = now_time.isoformat()
            result = client.describe_train_logs("train-51cd6bf7ec1000-37c5p5nlr01s-launcher",
                                                "2021-12-10T09:32:03.823509+00:00",
                                                now_time_str,
                                                limit=30)
            print(result)
            print(result.next())
            print(result.next())
            print(result.next())
        """
        result = self._describe_logs("TRAIN", pod_name, start_time, end_time, limit, order, context, filters)

        def get_next_data(xx):
            if result.Context != "":
                next_result = self.describe_train_logs(pod_name, start_time, end_time, limit, order, result.Context,
                                                       filters)
                result.Context = next_result.Context
                result.Content = next_result.Content
                result.RequestId = next_result.RequestId
                return result
            else:
                print("All logs are displayed! Return None.")
                return None

        result.next = types.MethodType(get_next_data, result)
        return result

    def create_text_dataset(self, dataset_name, storage_data_path, storage_label_path, dataset_tags=None):
        """创建文本数据集

        :param dataset_name: 数据集名称
        :type dataset_name: str
        :param storage_data_path: 数据源cos路径。格式：<bucket>/<cos path>/
        :type storage_data_path:  str
        :param storage_label_path: 数据集标签cos存储路径。格式：<bucket>/<cos path>/
        :type storage_label_path: str
        :param dataset_tags: 数据集标签
        :type dataset_tags: list of tikit.tencentcloud.tione.v20211111.models.Tag
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.CreateDatasetResponse`
        """
        try:
            req = models.CreateDatasetRequest()
            req.DatasetName = dataset_name
            req.DatasetType = "TYPE_DATASET_TEXT"
            req.StorageDataPath = self.parse_cos_info(storage_data_path)
            req.StorageLabelPath = self.parse_cos_info(storage_label_path)
            req.DatasetTags = dataset_tags
            return self._tione_client.CreateDataset(req)
        except TencentCloudSDKException as err:
            raise

    def create_table_dataset(self, dataset_name, storage_data_path, storage_label_path, dataset_tags=None,
                             is_schema_existed=None, schema_info_dict=None):
        """创建表格数据集

        :param dataset_name: 数据集名称
        :type dataset_name: str
        :param storage_data_path: 数据源cos路径。格式：<bucket>/<cos path>/
        :type storage_data_path:  str
        :param storage_label_path: 数据集标签cos存储路径。格式：<bucket>/<cos path>/
        :type storage_label_path:  str
        :param dataset_tags: 数据集标签
        :type dataset_tags: list of tikit.tencentcloud.tione.v20211111.models.Tag
        :param is_schema_existed: 数据是否存在表头。
            若数据文件包含表头，则请您严格按照表格列名配置Schema信息，否则校验不通过会造成导入数据集失败；
            若数据文件不包含表头，则平台会根据您定义的Schema信息依次为您解析表格数据
        :type is_schema_existed: bool
        :param schema_info_dict: 表头信息。格式： 字段名称 -> 数据类型。字段的数据类型包括：
            TYPE_INT:       整型
            TYPE_STRING:    字符串
            TYPE_BOOL:      布尔型
            TYPE_FLOAT:     浮点型
        :type dict
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.CreateDatasetResponse`
        """
        try:
            req = models.CreateDatasetRequest()
            req.DatasetName = dataset_name
            req.DatasetType = "TYPE_DATASET_TABLE"
            req.StorageDataPath = self.parse_cos_info(storage_data_path)
            req.StorageLabelPath = self.parse_cos_info(storage_label_path)
            req.DatasetTags = dataset_tags
            req.IsSchemaExisted = is_schema_existed
            req.SchemaInfos = []
            for field in schema_info_dict:
                schema_info = models.SchemaInfo()
                if field == "":
                    raise TencentCloudSDKException(message='schema_info.Name must be non-empty string')
                if schema_info_dict[field] not in SCHEMA_INFO_VALUE_TYPES:
                    raise TencentCloudSDKException(
                        message='schema_info.Type must in {}'.format(SCHEMA_INFO_VALUE_TYPES))
                schema_info.Name = field
                schema_info.Type = schema_info_dict[field]
                req.SchemaInfos.append(schema_info)
            return self._tione_client.CreateDataset(req)
        except TencentCloudSDKException as err:
            raise

    def create_image_dataset(self, dataset_name, storage_data_path, storage_label_path, dataset_tags=None,
                             with_annotation=False, annotation_type=None, annotation_format=None):
        """创建图片数据集

        :param dataset_name: 数据集名称
        :type dataset_name: str
        :param storage_data_path: 数据源cos路径。格式：<bucket>/<cos path>/
        :type storage_data_path:  str
        :param storage_label_path: 数据集标签cos存储路径。格式：<bucket>/<cos path>/
        :type storage_label_path:  str
        :param dataset_tags: 数据集标签
        :type dataset_tags: list of tikit.tencentcloud.tione.v20211111.models.Tag
        :param with_annotation: 是否已经标注
        :type with_annotation: bool
        :param annotation_type: 标注类型。可选值如下：
            ANNOTATION_TYPE_CLASSIFICATION: 图片分类
            ANNOTATION_TYPE_DETECTION:      目标检测
            ANNOTATION_TYPE_SEGMENTATION:   图片分割
            ANNOTATION_TYPE_TRACKING:       目标跟踪
        :type annotation_type: str
        :param annotation_format: 标注格式。可选值如下：
            ANNOTATION_FORMAT_TI:       TI平台格式
            ANNOTATION_FORMAT_PASCAL:   Pascal Voc
            ANNOTATION_FORMAT_COCO:     COCO
            ANNOTATION_FORMAT_FILE:     文件目录结构
        :type annotation_format: str
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.CreateDatasetResponse`
        """
        try:
            req = models.CreateDatasetRequest()
            req.DatasetName = dataset_name
            req.DatasetType = "TYPE_DATASET_IMAGE"
            req.StorageDataPath = self.parse_cos_info(storage_data_path)
            req.StorageLabelPath = self.parse_cos_info(storage_label_path)
            req.DatasetTags = dataset_tags
            req.AnnotationStatus = "STATUS_ANNOTATED" if with_annotation else "STATUS_NON_ANNOTATED"
            if with_annotation:
                if annotation_type not in ANNOTATION_TYPES:
                    raise TencentCloudSDKException(message='annotation_type must in {}'.format(ANNOTATION_TYPES))
                if annotation_format not in ANNOTATION_FORMATS:
                    raise TencentCloudSDKException(message='annotation_format must in {}'.format(ANNOTATION_FORMATS))
            req.AnnotationType = annotation_type
            req.AnnotationFormat = annotation_format
            return self._tione_client.CreateDataset(req)
        except TencentCloudSDKException as err:
            raise

    def create_other_dataset(self, dataset_name, storage_data_path, storage_label_path, dataset_tags=None):
        """创建其他类型的数据集

        :param dataset_name: 数据集名称
        :type dataset_name: str
        :param storage_data_path: 数据源cos路径。格式：<bucket>/<cos path>/
        :type storage_data_path:  str
        :param storage_label_path: 数据集标签cos存储路径。格式：<bucket>/<cos path>/
        :type storage_label_path:  str
        :param dataset_tags: 数据集标签
        :type dataset_tags: list of tikit.tencentcloud.tione.v20211111.models.Tag
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.CreateDatasetResponse`
        """
        try:
            req = models.CreateDatasetRequest()
            req.DatasetName = dataset_name
            req.DatasetType = "TYPE_DATASET_OTHER"
            req.StorageDataPath = self.parse_cos_info(storage_data_path)
            req.StorageLabelPath = self.parse_cos_info(storage_label_path)
            req.DatasetTags = dataset_tags
            return self._tione_client.CreateDataset(req)
        except TencentCloudSDKException as err:
            raise

    def describe_datasets(self, dataset_ids=None, filters=None, tag_filters=None, order=None, order_field=None,
                          offset=None, limit=None):
        """查看数据集列表

        :param dataset_ids: 数据集id列表
        :type dataset_ids: list of str
        :param filters: 字段过滤条件
        :type filters: list of Filter
        :param tag_filters: 标签过滤条件
        :type tag_filters: list of TagFilter
        :param order: 排序值 Asc Desc
        :type order: str
        :param order_field: 排序字段
        :type order_field: str
        :param offset: 偏移值
        :type offset: int
        :param limit: 返回数据个数
        :type limit: int
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.DescribeDatasetsResponse`
        """
        try:
            req = models.DescribeDatasetsRequest()
            req.DatasetIds = dataset_ids
            req.Filters = filters
            req.TagFilters = tag_filters
            req.Order = order
            req.OrderField = order_field
            req.Offset = offset
            req.Limit = limit
            return self._tione_client.DescribeDatasets(req)
        except TencentCloudSDKException as err:
            raise

    def delete_dataset(self, dataset_id, delete_label_enable=False):
        """删除某个数据集

        :param dataset_id: 数据集id
        :type dataset_id: str
        :param delete_label_enable: 是否删除cos标签文件
        :type delete_label_enable: bool
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.DeleteDatasetResponse`
        """
        try:
            req = models.DeleteDatasetRequest()
            req.DatasetId = dataset_id
            req.DeleteLabelEnable = delete_label_enable
            return self._tione_client.DeleteDataset(req)
        except TencentCloudSDKException as err:
            raise

    def describe_dataset_detail_structured(self, dataset_id, offset=None, limit=None):
        """查看结构化数据集的内容

        :param dataset_id: 数据集ID
        :type dataset_id: str
        :param offset: 偏移值
        :type offset: int
        :param limit: 返回数据条数
        :type limit: int
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.DescribeDatasetDetailStructuredResponse`
        """
        try:
            req = models.DescribeDatasetDetailStructuredRequest()
            req.DatasetId = dataset_id
            req.Offset = offset
            req.Limit = limit
            return self._tione_client.DescribeDatasetDetailStructured(req)
        except TencentCloudSDKException as err:
            raise

    def describe_dataset_detail_unstructured(self, dataset_id, offset=None, limit=None, label_list=[],
                                             annotation_status="STATUS_ALL"):
        """查看非结构化数据集的内容

        :param dataset_id: 数据集ID
        :type dataset_id: str
        :param offset: 偏移值
        :type offset: int
        :param limit: 返回数据条数
        :type limit: int
        :param label_list: 标签过滤参数
        :type label_list: list of str
        :param annotation_status: 标注状态过滤参数。
            STATUS_ANNOTATED:       已标注
            STATUS_NON_ANNOTATED:   未标注
            STATUS_ALL:             全部
        :type annotation_status: str
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.DescribeDatasetDetailUnstructuredResponse`
        """
        try:
            req = models.DescribeDatasetDetailUnstructuredRequest()
            req.DatasetId = dataset_id
            req.Offset = offset
            req.Limit = limit
            req.LabelList = label_list
            req.AnnotationStatus = annotation_status
            return self._tione_client.DescribeDatasetDetailUnstructured(req)
        except TencentCloudSDKException as err:
            raise

    def _get_model_index(self, task_id):
        req = models.DescribeLatestTrainingMetricsRequest()
        req.TaskId = task_id
        result = self._tione_client.DescribeLatestTrainingMetrics(req)
        indexs = []
        for metric in result.Metrics:
            indexs.append("{}={}".format(metric.MetricName, metric.Values[0].Value))
        return ",".join(indexs)

    def describe_system_reasoning_images(self):
        """获取平台内置的推理镜像

        :return:    推理镜像信息
        :rtype:     :class:`tikit.tencentcloud.tione.v20211111.models.DescribeInferTemplatesResponse`
        数据格式如：
        {
          "FrameworkTemplates": [
            {
              "Framework": "TENSORFLOW",
              "FrameworkVersion": "2.4",
              "Groups": [
                "TENSORFLOW",
                "LIGHT"
              ],
              "InferTemplates": [
                {
                  "InferTemplateId": "tf2.4-py38-cpu",
                  "InferTemplateImage": "ccr.ccs.tencentyun.com/qcloud-ti-platform/ti-cloud-infer-tensorflow-cpu:py38-tensorflow2.4-cpu-20211206"
                },
                {
                  "InferTemplateId": "tf2.4-py38-gpu",
                  "InferTemplateImage": "ccr.ccs.tencentyun.com/qcloud-ti-platform/ti-cloud-infer-tensorflow-gpu:py38-tensorflow2.4-cu110-20211206"
                }
              ]
            }
          ],
          "RequestId": "3654e19b-c2ba-4953-b131-d66495723008"
        }

        print的返回结果，输出如下：（镜像标识用来配置新模型的运行环境
        +------------+----------------+---------------------------------------------+----------------------------------+-------------------------------------------------------------------------------------------------------------+
        |  算法框架  | 算法框架版本号 |              支持的训练框架集合             |             镜像标识             |                                                   镜像url                                                   |
        +------------+----------------+---------------------------------------------+----------------------------------+-------------------------------------------------------------------------------------------------------------+
        | TENSORFLOW |      2.4       |           ['TENSORFLOW', 'LIGHT']           |          tf2.4-py38-cpu          |         ccr.ccs.tencentyun.com/qcloud-ti-platform/ti-cloud-infer-tensorflow-cpu:py38-tf2.4-cpu-1.0.0        |
        | TENSORFLOW |      2.4       |           ['TENSORFLOW', 'LIGHT']           |          tf2.4-py38-gpu          |       ccr.ccs.tencentyun.com/qcloud-ti-platform/ti-cloud-infer-tensorflow-cpu:py38-tf2.4-cu11.0-1.0.0       |
        | TENSORFLOW |      1.15      |      ['TENSORFLOW', 'LIGHT', 'TI_ACC']      |         tf1.15-py37-cpu          |        ccr.ccs.tencentyun.com/qcloud-ti-platform/ti-cloud-infer-tensorflow-cpu:py37-tf1.15-cpu-1.0.0        |
        | TENSORFLOW |      1.15      |      ['TENSORFLOW', 'LIGHT', 'TI_ACC']      |         tf1.15-py37-gpu          |       ccr.ccs.tencentyun.com/qcloud-ti-platform/ti-cloud-infer-tensorflow-cpu:py37-tf1.15-cu10.0-1.0.0      |
        |  PYTORCH   |      1.9       | ['PYTORCH', 'LIGHT', 'TI_ACC', 'AUTOML_CV'] |       torch1.9.0-py38-cpu        |        ccr.ccs.tencentyun.com/qcloud-ti-platform/ti-cloud-infer-pytorch-cpu:py38-torch1.9.0-cpu-1.0.0       |
        |  PYTORCH   |      1.9       | ['PYTORCH', 'LIGHT', 'TI_ACC', 'AUTOML_CV'] |      torch1.9.0-py38-cu111       |       ccr.ccs.tencentyun.com/qcloud-ti-platform/ti-cloud-infer-pytorch-gpu:py38-torch1.9.0-cu111-1.0.0      |
        |    PMML    |     0.9.12     |             ['SPARK', 'PYSPARK']            |           pypmml-py38            |             ccr.ccs.tencentyun.com/qcloud-ti-platform/ti-cloud-infer-pmml:py38-pmml0.9.16-1.0.0             |
        |    OCR     |      112       |                ['AUTOML_OCR']               |           ocr-serving            |                ccr.ccs.tencentyun.com/qcloud-ti-platform/ocr_serving_gpu:serving-v121-335584d               |
        | DETECTRON2 |      112       |                ['DETECTRON2']               | detectron2-torch1.9.0-py38-cu111 | ccr.ccs.tencentyun.com/qcloud-ti-platform/ti-cloud-infer-pytorch-gpu:py38-torch1.9.0-detectron2-cu111-1.0.0 |
        +------------+----------------+---------------------------------------------+----------------------------------+-------------------------------------------------------------------------------------------------------------+
        """
        req = models.DescribeInferTemplatesRequest()
        result = self._tione_client.DescribeInferTemplates(req)
        return result

    def _get_model_new_version(self, training_model_id):
        versions = self.describe_training_model_versions(training_model_id)
        ret = "v1"
        for version in versions.TrainingModelVersions:
            new_length = len(version.TrainingModelVersion)
            if new_length > len(ret) or (new_length == len(ret) and version.TrainingModelVersion > ret):
                ret = version.TrainingModelVersion
        return "v%d" % (int(ret[1:]) + 1)

    def _set_reaoning_env(self, req, reasoning_env):
        req.ReasoningEnvironmentSource = reasoning_env.Source 
        if reasoning_env.Source == "CUSTOM":
            req.ReasoningImageInfo = models.ImageInfo()
            req.ReasoningImageInfo.ImageType = reasoning_env.ImageType
            req.ReasoningImageInfo.ImageUrl = reasoning_env.ImageUrl
            req.ReasoningImageInfo.RegistryRegion = reasoning_env.RegistryRegion
            req.ReasoningImageInfo.RegistryId = reasoning_env.RegistryId
            return
        system_images = self.describe_system_reasoning_images()
        for framework_template in system_images.FrameworkTemplates:
            for image in framework_template.InferTemplates:
                if image.InferTemplateId == reasoning_env.ImageKey:
                    req.ReasoningEnvironment = image.InferTemplateImage
                    req.ReasoningEnvironmentId = reasoning_env.ImageKey
                    return
        raise TencentCloudSDKException(
            message='image key "{}" is invalid, please use "describe_system_images" to get image key list'.format(
                reasoning_env.ImageKey))

    def _check_model_clean_params(self, req, 
                                    model_auto_clean=None, 
                                    model_clean_period=None, 
                                    max_reserved_models=None):
        if model_auto_clean is not None:
            req.AutoClean = model_auto_clean
        if model_clean_period is not None:
            if model_clean_period == 0:
                req.ModelCleanPeriod = 65535
            else:
                req.ModelCleanPeriod = model_clean_period
        if max_reserved_models is not None:
            if max_reserved_models == 0:
                req.MaxReservedModels = 65535
            else:
                req.MaxReservedModels = max_reserved_models


    def _create_model_by_task(self,
                              req,
                              training_job_id,
                              reasoning_env,
                              model_output_path,
                              model_format,
                              training_model_index=None,
                              delete_task_cos_model=False,
                              tags=None,
                              training_model_cos_path=None,
                              is_qat=False):
        try:
            req.TrainingJobId = training_job_id
            req.TrainingModelSource = "JOB"
            self._set_cos_path_info(req, model_output_path)
            req.ModelFormat = model_format
            # get job info from task
            if model_format == "SAVED_MODEL":
                task = self.describe_training_task(training_job_id)
                req.TrainingJobName = task.TrainingTaskDetail.Name
                req.AlgorithmFramework = task.TrainingTaskDetail.FrameworkName
                if training_model_cos_path is not None:
                    self._set_task_cos_path_info(req, training_model_cos_path)
                else:
                    req.TrainingModelCosPath = task.TrainingTaskDetail.Output
                    req.TrainingModelCosPath.Paths[0] = req.TrainingModelCosPath.Paths[0] + training_job_id + "/"
            else:
                task = self.describe_training_task(training_job_id)
                req.TrainingJobName = task.TrainingTaskDetail.Name
                req.TrainingModelCosPath = task.TrainingTaskDetail.Output
                req.TrainingModelCosPath.Paths[0] = req.TrainingModelCosPath.Paths[0] + training_job_id + "/"
                req.AlgorithmFramework = task.TrainingTaskDetail.FrameworkName
            

            # 训练指标优先使用参数里面的
            req.TrainingModelIndex = training_model_index
            if not training_model_index:
                req.TrainingModelIndex = self._get_model_index(training_job_id)

            self._set_reaoning_env(req, reasoning_env)

            req.ModelMoveMode = "CUT" if delete_task_cos_model else "COPY"
            req.Tags = tags
            req.IsQAT = is_qat
            return self._tione_client.CreateTrainingModel(req)

        except TencentCloudSDKException as err:
            raise
    
    def _append_model_by_task(self,
                              req,
                              training_job_id,
                              training_model_cos_path,
                              reasoning_env,
                              model_format,
                              model_output_path,
                              training_model_index=None,
                              delete_task_cos_model=False,
                              tags=None,
                              is_qat=False):
        try:
            req.TrainingJobId = training_job_id
            req.TrainingModelSource = "JOB"
            self._set_cos_path_info(req, model_output_path)
            req.ModelFormat = model_format 
            # get job info from task
            if model_format == "SAVED_MODEL":
                task = self.describe_training_task(training_job_id)
                req.TrainingJobName = task.TrainingTaskDetail.Name
                req.AlgorithmFramework = task.TrainingTaskDetail.FrameworkName
                if training_model_cos_path is not None:
                    self._set_task_cos_path_info(req, training_model_cos_path)
            else:
                task = self.describe_training_task(training_job_id)
                req.TrainingJobName = task.TrainingTaskDetail.Name
                req.TrainingModelCosPath = task.TrainingTaskDetail.Output
                req.TrainingModelCosPath.Paths[0] = req.TrainingModelCosPath.Paths[0] + training_job_id + "/"
                req.AlgorithmFramework = task.TrainingTaskDetail.FrameworkName

            # 训练指标优先使用参数里面的
            req.TrainingModelIndex = training_model_index
            if not training_model_index:
                req.TrainingModelIndex = self._get_model_index(training_job_id)
            
            self._set_reaoning_env(req, reasoning_env)

            req.ModelMoveMode = "CUT" if delete_task_cos_model else "COPY"
            req.Tags = tags
            req.IsQAT = is_qat
            return self._tione_client.CreateTrainingModel(req)
        except TencentCloudSDKException as err:
            raise

    def _set_cos_path_info(self, req, model_output_path):
        if isinstance(model_output_path, str):
            req.ModelOutputPath = self.parse_cos_info(model_output_path)
            return
        cos_path_info = models.CosPathInfo()
        cos_path_info.Bucket = model_output_path.Bucket
        cos_path_info.Region = model_output_path.Region
        cos_path_info.Paths = [model_output_path.Path]
        cos_path_info.Uin = model_output_path.Uin
        cos_path_info.SubUin = model_output_path.SubUin
        req.ModelOutputPath = cos_path_info
    
    def _set_task_cos_path_info(self, req, training_model_cos_path):
        if isinstance(training_model_cos_path, str):
            req.TrainingModelCosPath = self.parse_cos_info(training_model_cos_path)
            return
        cos_path_info = models.CosPathInfo()
        cos_path_info.Bucket = training_model_cos_path.Bucket
        cos_path_info.Region = training_model_cos_path.Region
        cos_path_info.Paths = [training_model_cos_path.Path]
        cos_path_info.Uin = training_model_cos_path.Uin
        cos_path_info.SubUin = training_model_cos_path.SubUin
        req.TrainingModelCosPath = cos_path_info

    def create_model_by_task(self,
                             training_model_name,
                             training_job_id,
                             reasoning_env,
                             model_output_path,
                             model_format,
                             training_model_index=None,
                             delete_task_cos_model=False,
                             tags=None,
                             training_model_cos_path=None,
                             model_auto_clean=None,
                             model_clean_period=None,
                             max_reserved_models=None,
                             is_qat=False):
        """使用现有的任务的结果来创建新的模型

        :param training_model_name: 模型名称
        :type training_model_name: str
        :param training_job_id: 训练任务id
        :type training_job_id: str
        :param reasoning_env: 推理的运行环境
        :type reasoning_env:  :class:`tikit.models.ReasoningEnvironment`
        :param model_output_path: 模型输出路径
        :type model_output_path: str or :class:`tikit.models.CosPathInfo`
        :param model_format: 模型格式(PYTORCH/TORCH_SCRIPT/DETECTRON2/SAVED_MODEL/FROZEN_GRAPH/PMML/MMDETECTION/ONNX/HUGGING_FACE)
        :type model_format: str
        :param training_model_index: 训练指标。非空时覆盖训练任务里面的值。
        :type training_model_index: str
        :param delete_task_cos_model: 是否删除任务的原来输出的模型文件
        :type delete_task_cos_model: bool
        :param tags: 标签配置
        :type tags: list of :class:`tikit.tencentcloud.tione.v20211111.models.Tag`
        :param training_model_cos_path: 模型来源路径(当模型格式为SAVED_MODEL时，必传)
        :type training_model_cos_path: str or :class:`tikit.models.CosPathInfo` 
        :param model_auto_clean: 模型自动清理开关(true/false)，当前版本仅支持SAVED_MODEL格式模型
        :type model_auto_clean: str 
        :param model_clean_period: 模型清理周期(默认值为1分钟，上限为1440，下限为1分钟，步长为1)
        :type model_clean_period: int
        :param max_reserved_models: 模型数量保留上限(默认值为24个，上限为24，下限为1，步长为1)
        :type max_reserved_models: int
        :param is_qat: 是否qat模型，默认为否
        :type is_qat: bool
        :return:
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.CreateTrainingModelResponse`
        返回的内容如下：
        {
          "Id": "m-23054253294030848",
          "TrainingModelVersionId": "mv-v1-558585918986294017",
          "RequestId": "c8f9b70e-bf4d-4c34-9480-21d902d3b341"
        }
        """
        req = models.CreateTrainingModelRequest()
        req.ImportMethod = "MODEL"
        req.TrainingModelName = training_model_name

        self._check_model_clean_params(req, model_auto_clean, model_clean_period, max_reserved_models)

        return self._create_model_by_task(req,
                                          training_job_id,
                                          reasoning_env,
                                          model_output_path,
                                          model_format,
                                          training_model_index,
                                          delete_task_cos_model,
                                          tags,
                                          training_model_cos_path, is_qat)

    def create_model_by_cos(self,
                            training_model_name,
                            algorithm_framework,
                            model_cos_path,
                            reasoning_env,
                            model_format,
                            model_index=None,
                            model_output_path=None,
                            delete_task_cos_model=False,
                            tags=None,
                            model_auto_clean=None,
                            model_clean_period=None,
                            max_reserved_models=None,
                            is_qat=False):
        """使用现有的cos路径来创建新的模型


        :param training_model_name: 模型名称
        :type training_model_name: str
        :param algorithm_framework: 算法框架
        :type algorithm_framework: str
        :param model_cos_path: 模型cos目录，以/结尾。格式：<bucket>/<cos path>/
        :type model_cos_path:  str
        :param reasoning_env: 推理的运行环境
        :type reasoning_env:  :class:`tikit.models.ReasoningEnvironment`
        :param model_format: 模型格式(PYTORCH/TORCH_SCRIPT/DETECTRON2/SAVED_MODEL/FROZEN_GRAPH/PMML/MMDETECTION/ONNX/HUGGING_FACE)
        :type model_format: str
        :param model_index: 训练指标。
        :type model_index: str
        :param model_output_path: 模型输出路径
        :type model_output_path: :class:`tikit.models.CosPathInfo`
        :param delete_task_cos_model: 是否删除任务的原来输出的模型文件
        :type delete_task_cos_model: bool
        :param tags: 标签配置
        :type tags: list of :class:`tikit.tencentcloud.tione.v20211111.models.Tag`
        :param model_auto_clean: 模型自动清理开关(true/false)，当前版本仅支持SAVED_MODEL格式模型
        :type model_auto_clean: str 
        :param model_clean_period: 模型清理周期(默认值为1分钟，上限为1440，下限为1分钟，步长为1)
        :type model_clean_period: int
        :param max_reserved_models: 模型数量保留上限(默认值为24个，上限为24，下限为1，步长为1)
        :type max_reserved_models: int
        :param is_qat: 是否qat模型，默认为否
        :type is_qat: bool
        :return:
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.CreateTrainingModelResponse`
        返回的内容如下：
        {
          "Id": "m-23054253294030848",
          "TrainingModelVersionId": "mv-v1-558585918986294017",
          "RequestId": "c8f9b70e-bf4d-4c34-9480-21d902d3b341"
        }
        """
        try:
            req = models.CreateTrainingModelRequest()
            req.ImportMethod = "MODEL"
            req.TrainingModelName = training_model_name
            req.AlgorithmFramework = algorithm_framework
            self._set_task_cos_path_info(req, model_cos_path)
            req.TrainingModelIndex = model_index
            req.TrainingModelSource = "COS"

            # 设置镜像
            self._set_reaoning_env(req, reasoning_env)

            #self._set_cos_path_info(req, model_output_path)
            req.ModelFormat = model_format

            req.ModelMoveMode = "CUT" if delete_task_cos_model else "COPY"
            req.Tags = tags
            req.IsQAT = is_qat

            self._check_model_clean_params(req, model_auto_clean, model_clean_period, max_reserved_models) 
            return self._tione_client.CreateTrainingModel(req)
        except TencentCloudSDKException as err:
            raise

    def create_model_version_by_task(self,
                                     training_model_id,
                                     training_job_id,
                                     reasoning_env,
                                     model_output_path,
                                     model_format,
                                     training_model_index=None,
                                     delete_task_cos_model=False,
                                     tags=None,
                                     training_model_cos_path=None,
                                     model_auto_clean=None,
                                     model_clean_period=None,
                                     max_reserved_models=None,
                                     is_qat=False):
        """使用现有的任务来创建新的模型版本

        :param training_model_id: 模型ID
        :type training_model_id: str
        :param training_job_id: 训练任务id
        :type training_job_id: str
        :param reasoning_env: 推理的运行环境
        :type reasoning_env:  :class:`tikit.models.ReasoningEnvironment`
        :param model_output_path: 模型输出路径
        :type model_output_path: :class:`tikit.models.CosPathInfo`
        :param model_format: 模型格式(PYTORCH/TORCH_SCRIPT/DETECTRON2/SAVED_MODEL/FROZEN_GRAPH/PMML/MMDETECTION/ONNX/HUGGING_FACE)
        :type model_format: str
        :param training_model_index: 训练指标。非空时覆盖训练任务里面的值。
        :type training_model_index: str
        :param delete_task_cos_model: 是否删除任务的原来输出的模型文件
        :type delete_task_cos_model: bool
        :param tags: 标签配置
        :param training_model_cos_path: 模型来源路径(当模型格式为SAVED_MODEL时，必传)
        :type training_model_cos_path: :class:`tikit.models.CosPathInfo` 
        :type tags: list of :class:`tikit.tencentcloud.tione.v20211111.models.Tag`
        :param model_auto_clean: 模型自动清理开关(true/false)，当前版本仅支持SAVED_MODEL格式模型
        :type model_auto_clean: str 
        :param model_clean_period: 模型清理周期(默认值为1分钟，上限为1440，下限为1分钟，步长为1)
        :type model_clean_period: int
        :param max_reserved_models: 模型数量保留上限(默认值为24个，上限为24，下限为1，步长为1)
        :type max_reserved_models: int
        :param is_qat: 是否qat模型，默认为否
        :type is_qat: bool
        :return:
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.CreateTrainingModelResponse`
        返回的内容如下：
        {
          "Id": "m-23054253294030848",
          "TrainingModelVersionId": "mv-v1-558585918986294017",
          "RequestId": "c8f9b70e-bf4d-4c34-9480-21d902d3b341"
        }
        """
        try:
            req = models.CreateTrainingModelRequest()
            req.ImportMethod = "VERSION"
            req.TrainingModelId = training_model_id
            req.TrainingModelVersion = self._get_model_new_version(training_model_id)
            self._check_model_clean_params(req, model_auto_clean, model_clean_period, max_reserved_models)
            return self._create_model_by_task(req,
                                              training_job_id,
                                              reasoning_env,
                                              model_output_path,
                                              model_format,
                                              training_model_index,
                                              delete_task_cos_model,
                                              tags,
                                              training_model_cos_path,
                                              is_qat)
        except TencentCloudSDKException as err:
            raise

    def create_model_version_by_cos(self,
                                    training_model_id,
                                    algorithm_framework,
                                    model_cos_path,
                                    reasoning_env,
                                    model_format,
                                    model_output_path=None,
                                    model_index=None,
                                    delete_task_cos_model=False,
                                    tags=None,
                                    model_auto_clean=None,
                                    model_clean_period=None,
                                    max_reserved_models=None,
                                    is_qat=False):
        """使用现有的cos路径来创建新的模型版本

        :param training_model_id: 模型ID
        :type training_model_id: str
        :param algorithm_framework: 算法框架
        :type algorithm_framework: str
        :param model_cos_path: 模型cos目录，以/结尾。格式：<bucket>/<cos path>/
        :type model_cos_path:  str
        :param reasoning_env: 推理的运行环境
        :type reasoning_env:  :class:`tikit.models.ReasoningEnvironment`
        :param model_format: 模型格式(PYTORCH/TORCH_SCRIPT/DETECTRON2/SAVED_MODEL/FROZEN_GRAPH/PMML/MMDETECTION/ONNX/HUGGING_FACE)
        :type model_format: str
        :param model_output_path: 模型输出路径
        :type model_output_path: :class:`tikit.models.CosPathInfo`
        :param model_index: 训练指标。
        :type model_index: str
        :param delete_task_cos_model: 是否删除任务的原来输出的模型文件
        :type delete_task_cos_model: bool
        :param tags: 标签配置
        :type tags: list of :class:`tikit.tencentcloud.tione.v20211111.models.Tag`
        :param model_auto_clean: 模型自动清理开关(true/false)，当前版本仅支持SAVED_MODEL格式模型
        :type model_auto_clean: str 
        :param model_clean_period: 模型清理周期(默认值为1分钟，上限为1440，下限为1分钟，步长为1)
        :type model_clean_period: int
        :param max_reserved_models: 模型数量保留上限(默认值为24个，上限为24，下限为1，步长为1)
        :type max_reserved_models: int
        :param is_qat: 是否qat模型，默认为否
        :type is_qat: bool
        :return:
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.CreateTrainingModelResponse`
        返回的内容如下：
        {
          "Id": "m-23054253294030848",
          "TrainingModelVersionId": "mv-v1-558585918986294017",
          "RequestId": "c8f9b70e-bf4d-4c34-9480-21d902d3b341"
        }
        """
        try:
            req = models.CreateTrainingModelRequest()
            req.ImportMethod = "VERSION"
            req.TrainingModelId = training_model_id
            req.TrainingModelVersion = self._get_model_new_version(training_model_id)
            req.AlgorithmFramework = algorithm_framework
            self._set_task_cos_path_info(req, model_cos_path)
            req.TrainingModelIndex = model_index
            req.TrainingModelSource = "COS"

            #self._set_cos_path_info(req, model_output_path)
            req.ModelFormat = model_format

            req.ModelMoveMode = "CUT" if delete_task_cos_model else "COPY"

            self._set_reaoning_env(req, reasoning_env)

            req.Tags = tags
            req.IsQAT = is_qat
            self._check_model_clean_params(req, model_auto_clean, model_clean_period, max_reserved_models)
            
            return self._tione_client.CreateTrainingModel(req)
        except TencentCloudSDKException as err:
            raise

    def append_model_into_existed_version_by_task(self, 
                                          training_model_id,
                                          training_model_version,
                                          training_job_id,
                                          training_model_cos_path,
                                          reasoning_env,
                                          model_output_path,
                                          model_format,
                                          training_model_index=None,
                                          model_auto_clean=None,
                                          model_clean_period=None, 
                                          max_reserved_models=None,
                                          delete_task_cos_model=False,
                                          tags=None,
                                          is_qat=False):
        """使用现有的任务的结果来追加模型至现有的模型版本

        :param training_model_id: 模型ID
        :type training_model_id: str
        :param training_model_version: 模型版本
        :type training_model_version: str
        :param training_job_id: 训练任务id
        :type training_job_id: str
        :param training_model_cos_path: 模型来源路径
        :type training_model_cos_path: :class:`tikit.models.CosPathInfo` 
        :param reasoning_env: 推理的运行环境
        :type reasoning_env:  :class:`tikit.models.ReasoningEnvironment`
        :param model_output_path: 模型输出路径
        :type model_output_path: :class:`tikit.models.CosPathInfo`
        :param model_format: 模型格式(PYTORCH/TORCH_SCRIPT/DETECTRON2/SAVED_MODEL/FROZEN_GRAPH/PMML/MMDETECTION/ONNX/HUGGING_FACE)
        :type model_format: str
        :param training_model_index: 训练指标。非空时覆盖训练任务里面的值。
        :type training_model_index: str
        :param model_auto_clean: 模型自动清理开关(true/false)，当前版本仅支持SAVED_MODEL格式模型
        :type model_auto_clean: str 
        :param model_clean_period: 模型清理周期(默认值为1分钟，上限为1440，下限为1分钟，步长为1)
        :type model_clean_period: int
        :param max_reserved_models: 模型数量保留上限(默认值为24个，上限为24，下限为1，步长为1)
        :type max_reserved_models: int
        :param delete_task_cos_model: 是否删除任务的原来输出的模型文件
        :type delete_task_cos_model: bool
        :param tags: 标签配置
        :type tags: list of :class:`tikit.tencentcloud.tione.v20211111.models.Tag`
        :param is_qat: 是否qat模型，默认为否
        :type is_qat: bool
        :return:
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.CreateTrainingModelResponse`
        返回的内容如下：
        {
          "Id": "m-23054253294030848",
          "TrainingModelVersionId": "mv-v1-558585918986294017",
          "RequestId": "c8f9b70e-bf4d-4c34-9480-21d902d3b341"
        }
        """
        try:
            req = models.CreateTrainingModelRequest()
            req.ImportMethod = "EXIST"
            req.TrainingModelId = training_model_id
            req.TrainingModelVersion = training_model_version

            self._check_model_clean_params(req, model_auto_clean, model_clean_period, max_reserved_models)
            return self._append_model_by_task(req,
                                              training_job_id,
                                              training_model_cos_path,
                                              reasoning_env,
                                              model_format,
                                              model_output_path,
                                              training_model_index,
                                              delete_task_cos_model,
                                              tags,
                                              is_qat)
            
        except TencentCloudSDKException as err:
            raise

    def append_model_into_existed_version_by_cos(self,
                                                  training_model_id,
                                                  training_model_version,
                                                  model_cos_path,
                                                  model_format,
                                                  reasoning_env,
                                                  model_output_path,
                                                  model_index=None,
                                                  model_auto_clean=None,
                                                  model_clean_period=None,
                                                  max_reserved_models=None,
                                                  delete_task_cos_model=False,
                                                  tags=None,
                                                  is_qat=False):
        """使用现有的cos路径来更新现有模型版本的模型
        :param training_model_id: 模型ID
        :type training_model_id: str
        :param training_model_version: 模型版本
        :type training_model_version: str
        :param model_cos_path: 模型来源路径
        :type model_cos_path: :class:`tikit.models.CosPathInfo`
        :param model_format: 模型格式(PYTORCH/TORCH_SCRIPT/DETECTRON2/SAVED_MODEL/FROZEN_GRAPH/PMML/MMDETECTION/ONNX/HUGGING_FACE)
        :type model_format: str
        :param reasoning_env: 推理的运行环境
        :type reasoning_env:  :class:`tikit.models.ReasoningEnvironment`
        :param model_output_path: 模型输出路径
        :type model_output_path: :class:`tikit.models.CosPathInfo`
        :param model_index: 训练指标。非空时覆盖训练任务里面的值。
        :type model_index: str
        :param model_auto_clean: 模型自动清理开关(true/false)，当前版本仅支持SAVED_MODEL格式模型
        :type model_auto_clean: str 
        :param model_clean_period: 模型清理周期(默认值为1分钟，上限为1440，下限为1分钟，步长为1)
        :type model_clean_period: int
        :param max_reserved_models: 模型数量保留上限(默认值为24个，上限为24，下限为1，步长为1)
        :type max_reserved_models: int
        :param delete_task_cos_model: 是否删除任务的原来输出的模型文件
        :type delete_task_cos_model: bool
        :param tags: 标签配置
        :type tags: list of :class:`tikit.tencentcloud.tione.v20211111.models.Tag`
        :param is_qat: 是否qat模型，默认为否
        :type is_qat: bool
        :return:
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.CreateTrainingModelResponse`
        返回的内容如下：
        {
          "Id": "m-23054253294030848",
          "TrainingModelVersionId": "mv-v1-558585918986294017",
          "RequestId": "c8f9b70e-bf4d-4c34-9480-21d902d3b341"
        }
        """
        try:
            req = models.CreateTrainingModelRequest()
            req.ImportMethod = "EXIST"
            req.TrainingModelId = training_model_id
            req.TrainingModelVersion = training_model_version
            req.ModelFormat = model_format
            if model_index is not None:
                req.TrainingModelIndex = model_index
            self._set_task_cos_path_info(req, model_cos_path)
            req.TrainingModelSource = "COS"

            self._check_model_clean_params(req, model_auto_clean, model_clean_period, max_reserved_models)

            # 设置镜像
            self._set_reaoning_env(req, reasoning_env)

            self._set_cos_path_info(req, model_output_path)

            req.ModelMoveMode = "CUT" if delete_task_cos_model else "COPY"
            req.Tags = tags
            req.IsQAT = is_qat
            return self._tione_client.CreateTrainingModel(req)
        except TencentCloudSDKException as err:
            raise

    def describe_training_models(self, filters=None, order_field=None, order=None, offset=None, limit=None,
                                 tag_filters=None):
        """查看模型列表

        :param filters: 过滤器
        :type filters: list of Filter
        :param order_field: 排序字段
        :type order_field: str
        :param order: 排序方式，ASC/DESC
        :type order: str
        :param offset: 偏移量
        :type offset: int
        :param limit: 返回结果数量
        :type limit: int
        :param tag_filters: 标签过滤
        :type tag_filters: list of TagFilter
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.DescribeTrainingModelsResponse`
        print返回的结果，输出如下：
        +---------------------+-----------------------+-------------------+----------------------+
        |        模型ID       |          名称         |        标签       |       创建时间       |
        +---------------------+-----------------------+-------------------+----------------------+
        | m-23054253294030848 | tikit-model-task-1227 |                   | 2021-12-27T14:22:43Z |
        | m-23054252760240128 |  tikit-model-cos-1227 |                   | 2021-12-27T14:22:35Z |
        | m-23054246746066944 |    tikit-model-task   |                   | 2021-12-27T14:21:03Z |
        | m-23037023501881344 |    tikit-model-cos    |                   | 2021-12-24T13:20:57Z |
        | m-23036973226987520 |   tikit-model-name-2  |                   | 2021-12-24T13:08:10Z |
        | m-23034443699064832 |      model_cos-2      |                   | 2021-12-24T02:24:52Z |
        | m-23028904650346496 |       model_cos       | tag_001:tag_v_001 | 2021-12-23T02:56:13Z |
        | m-23028894739075072 |       model_task      |                   | 2021-12-23T02:53:42Z |
        |  22997374387884032  |         xx004         |                   | 2021-12-17T13:17:39Z |
        |  22996889833377792  |         xx003         |                   | 2021-12-17T11:14:26Z |
        +---------------------+-----------------------+-------------------+----------------------+
        """
        try:
            req = models.DescribeTrainingModelsRequest()
            req.Filters = filters
            req.OrderField = order_field
            req.Order = order
            req.Offset = offset
            req.Limit = limit
            req.TagFilters = tag_filters
            return self._tione_client.DescribeTrainingModels(req)
        except TencentCloudSDKException as err:
            raise

    def describe_training_model_versions(self, training_model_id):
        """查看模型各个版本的信息列表

        :param training_model_id: 模型id
        :type training_model_id: str
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.DescribeTrainingModelVersionsResponse`
        """
        try:
            req = models.DescribeTrainingModelVersionsRequest()
            req.TrainingModelId = training_model_id
            return self._tione_client.DescribeTrainingModelVersions(req)
        except TencentCloudSDKException as err:
            raise

    def describe_training_model_version(self, training_model_version_id):
        """查看单个版本的信息

        :param training_model_version_id: 模型版本id
        :type training_model_version_id: str
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.DescribeTrainingModelVersionResponse`
        """
        try:
            req = models.DescribeTrainingModelVersionRequest()
            req.TrainingModelVersionId = training_model_version_id
            return self._tione_client.DescribeTrainingModelVersion(req)
        except TencentCloudSDKException as err:
            raise

    def delete_training_model(self, training_model_id,enable_delete_cos=True,model_version_type=None):
        """删除模型

        :param training_model_id: 模型id
        :type training_model_id: str
        :param enable_delete_cos: 是否同步清理cos
        :type enable_delete_cos: bool
        :param model_version_type: 删除模型类型，枚举值：NORMAL 普通，ACCELERATE 加速，不传则删除所有
        :type model_version_type: str
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.DeleteTrainingModelResponse`
        """
        try:
            req = models.DeleteTrainingModelRequest()
            req.TrainingModelId = training_model_id
            req.EnableDeleteCos = enable_delete_cos
            if model_version_type is not None:
                req.ModelVersionType = model_version_type
            return self._tione_client.DeleteTrainingModel(req)
        except TencentCloudSDKException as err:
            raise

    def delete_training_model_version(self, training_model_version_id, enable_delete_cos=True):
        """删除模型版本

        :param training_model_version_id: 模型版本id
        :type training_model_version_id: str
        :param enable_delete_cos: 是否同步清理cos
        :type enable_delete_cos: bool
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.DeleteTrainingModelVersionResponse`
        """
        try:
            req = models.DeleteTrainingModelVersionRequest()
            req.TrainingModelVersionId = training_model_version_id
            req.EnableDeleteCos = enable_delete_cos
            return self._tione_client.DeleteTrainingModelVersion(req)
        except TencentCloudSDKException as err:
            raise

    def create_batch_task(self,
                          batch_task_name,
                          job_type,
                          resource_config_info,
                          output_config,
                          input_config,
                          resource_group_id=None,
                          model_info=None,
                          image_info=None,
                          code_package=None,
                          start_cmd=None,
                          cron_info=None,
                          tags=None,
                          log_enable=False,
                          log_logset_id=None,
                          log_topic_id=None,
                          vpc_id=None,
                          subnet_id=None,
                          remark=""):
        """创建跑批任务

        :param batch_task_name: 跑批任务名称
        :type batch_task_name: str
        :param job_type: 工作类型 1:单次 2:周期
        :param job_type: int
        :param resource_config_info: 资源配置
        :type resource_config_info: :class:`tikit.models.ResourceConfigInfo`
        :param output_config: 输出数据源配置（如果为COS，目标路径需为空字符串）
        :type output_config: list or :class:`tikit.models.TrainingDataConfig`
        :param input_config: 输入数据源配置（如果为COS，目标路径需为空字符串）
        :type input_config: list or :class:`tikit.models.TrainingDataConfig`
        :param resource_group_id:   预付费的资源组id
        :type resource_group_id:    str
        :param model_info: 模型信息
        :type model_info: :class:`tikit.models.ModelInfo`
        :param image_info: 镜像信息（如果使用模型文件，则自定义镜像无效）
        :type image_info: :class:`tikit.models.FrameworkInfo`
        :param code_package: 代码的cos信息
        :type code_package: str
        :param start_cmd: 启动命令
        :type start_cmd: str
        :param cron_info: 周期配置信息
        :type cron_info: :class:`tikit.tencentcloud.tione.v20211111.models.CronInfo`
        :param tags: 标签
        :type tags: list of :class:`tikit.tencentcloud.tione.v20211111.models.Tag`
        :param log_enable: 日志开关
        :type log_enable: bool
        :param log_logset_id: 日志集id
        :type log_logset_id: str
        :param log_topic_id: 日志topic id
        :type log_topic_id: str
        :param vpc_id: vpc id
        :type vpc_id: str
        :param subnet_id:
        :type subnet_id: 子网id
        :param remark: 任务描述
        :type remark: str
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.CreateBatchTaskResponse`
        """
        try:
            req = models.CreateBatchTaskRequest()
            req.BatchTaskName = batch_task_name
            req.JobType = job_type
            if job_type == 2:
                if cron_info is None:
                    raise Exception("cron_info is required")
                req.CronInfo = cron_info

            parse_input_configs, _ = self._parse_training_task_input_data(self._parse_batch_cos_target_path(input_config))
            if len(parse_input_configs) > 1:
                raise Exception("input_config max length is 1")
            req.DataConfigs = parse_input_configs

            parse_output_configs, _ = self._parse_training_task_input_data(self._parse_batch_cos_target_path(output_config))
            if len(parse_output_configs) > 1:
                raise Exception("output_config max length is 1")
            req.Outputs = parse_output_configs

            if parse_input_configs[0].DataSourceType == "WEDATA_HDFS" or parse_output_configs[0].DataSourceType == "WEDATA_HDFS":
                if vpc_id is None or subnet_id is None:
                    raise Exception("WEDATA_HDFS require vpc_id and subnet_id")

            imageInfo = models.ImageInfo()
            if model_info is not None:
                if code_package is not None:
                    req.CodePackage = self.parse_cos_info(code_package)

                modelInfo = models.ModelInfo()
                if model_info.ModelType == "NORMAL":
                    model_list = self.describe_training_models(
                        filters=self._new_name_filter("keyword", model_info.ModelName))
                    if model_list.TotalCount == 0:
                        raise Exception("model %s not found" % model_info.ModelName)

                    model_tmp = model_list.TrainingModels[0]
                    modelInfo.ModelName = model_info.ModelName
                    modelInfo.ModelType = model_info.ModelType
                    modelInfo.ModelId = model_tmp.TrainingModelId

                    model_version_list = self.describe_training_model_versions(modelInfo.ModelId)
                    for model_version_tmp in model_version_list.TrainingModelVersions:
                        if model_version_tmp.TrainingModelVersion == model_info.ModelVersion:
                            modelInfo.ModelVersion = model_info.ModelVersion
                            modelInfo.ModelVersionId = model_version_tmp.TrainingModelVersionId
                            modelInfo.AlgorithmFramework = model_version_tmp.AlgorithmFramework
                            modelInfo.CosPathInfo = model_version_tmp.TrainingModelCosPath
                            modelInfo.ModelSource = model_version_tmp.TrainingModelSource

                            imageInfo.ImageUrl = model_version_tmp.ReasoningEnvironment
                            imageInfo.ImageType = model_version_tmp.ReasoningEnvironmentSource
                            break
                    if modelInfo.ModelVersionId is None:
                        raise Exception("model version %s not found" % model_info.ModelVersion)
                elif model_info.ModelType == "ACCELERATE":
                    model_list = self._describe_model_accelerate_versions(
                        filters=self._new_name_filter("ModelJobName", model_info.ModelName))
                    if model_list.TotalCount == 0:
                        raise Exception("model %s not found" % model_info.ModelName)
                    for model_tmp in model_list.ModelAccelerateVersions:
                        if model_tmp.ModelVersion == model_info.ModelVersion:
                            modelInfo.ModelType = model_info.ModelType
                            modelInfo.ModelId = model_tmp.ModelId
                            modelInfo.ModelVersion = model_tmp.ModelVersion
                            modelInfo.ModelVersionId = model_tmp.ModelVersionId
                            modelInfo.CosPathInfo = model_tmp.ModelCosPath

                            modelInfo.ModelName = model_tmp.ModelSource.ModelName
                            modelInfo.ModelSource = model_tmp.ModelSource.Source
                            modelInfo.AlgorithmFramework = model_tmp.ModelSource.AlgorithmFramework
                            imageInfo.ImageUrl = model_tmp.ModelSource.ReasoningEnvironment
                            imageInfo.ImageType = model_tmp.ModelSource.ReasoningEnvironmentSource
                            break
                    if modelInfo.ModelId is None:
                        raise Exception("model %s not found" % model_info.ModelName)
                req.ModelInfo = modelInfo
            else:
                if image_info is None:
                    raise Exception("image_info is required")
                imageInfo.ImageType = image_info.ImageType
                imageInfo.ImageUrl = image_info.ImageUrl
                imageInfo.RegistryRegion = image_info.RegistryRegion
                imageInfo.RegistryId = image_info.RegistryId

            if imageInfo.ImageType == "SYSTEM":
                imageInfo.ImageType = "PRESET"
            req.ImageInfo = imageInfo

            req.ChargeType = resource_config_info.ChargeType
            worker_info = models.ResourceConfigInfo()
            worker_info.Role = "WORKER"
            worker_info.InstanceNum = 1
            if req.ChargeType == "PREPAID":
                if resource_group_id is None:
                    raise Exception("resource_group_id is required")
                req.ResourceGroupId = resource_group_id
                worker_info.Cpu = resource_config_info.Cpu
                worker_info.Memory = resource_config_info.Memory
                worker_info.GpuType = resource_config_info.GpuType
                worker_info.Gpu = resource_config_info.Gpu
            else:
                billing_spec_req = models.DescribeBillingSpecsRequest()
                billing_spec_req.TaskType = "INFERENCE"
                billing_spec_req.ChargeType = "POSTPAID_BY_HOUR"
                billing_spec_req.ResourceType = "CALC"
                if imageInfo.ImageType == "PRESET":
                    if "gpu" in imageInfo.ImageUrl:
                        billing_spec_req.ResourceType = "GPU"
                    if "cpu" in imageInfo.ImageUrl:
                        billing_spec_req.ResourceType = "CPU"
                billing_spec = self._tione_client.DescribeBillingSpecs(billing_spec_req)
                for v in billing_spec.Specs:
                    if resource_config_info.InstanceType == v.SpecName:

                        worker_info.InstanceType = resource_config_info.InstanceType
                        worker_info.InstanceTypeAlias = v.SpecAlias
                        break
                if worker_info.InstanceType is None:
                    raise Exception("please enter the correct InstanceType: %s" % resource_config_info.InstanceType)
            req.ResourceConfigInfo = worker_info

            req.StartCmd = start_cmd

            if log_enable:
                req.LogConfig = models.LogConfig()
                req.LogConfig.LogsetId = log_logset_id
                req.LogConfig.TopicId = log_topic_id
            req.LogEnable = log_enable

            req.Tags = tags

            req.VpcId = vpc_id
            req.SubnetId = subnet_id
            req.Remark = remark

            return self._tione_client.CreateBatchTask(req)
        except TencentCloudSDKException as err:
            raise

    def _parse_batch_cos_target_path(self, data_config):
        if not isinstance(data_config, list):
            # 兼容旧版本的处理
            if data_config.DataSource == "COS":
                for cos_str in data_config.DataConfigDict:
                    data_config.DataConfigDict[cos_str] = ""
            return data_config

        for i in range(len(data_config)):
            if data_config[i].DataSource == "COS":
                data_config[i].TargetPath = ""
        return data_config

    def describe_batch_task(self, batch_task_id):
        """查询跑批任务

        :param batch_task_id:
        :type batch_task_id: str
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.DescribeBatchTaskResponse`
        """
        try:
            req = models.DescribeBatchTaskRequest()
            req.BatchTaskId = batch_task_id
            return self._tione_client.DescribeBatchTask(req)
        except TencentCloudSDKException as err:
            raise

    def describe_batch_tasks(self, filters=None, tag_filters=None, offset=0, limit=50, order="DESC",
                             order_field="UpdateTime"):
        """获取跑批任务列表

        :param filters:     过滤器，eg：[{ "Name": "TaskStatus", "Values": ["Running"] }]
        :type filters:      list of Filter
        :param tag_filters: 标签过滤器，eg：[{ "TagKey": "TagKeyA", "TagValue": ["TagValueA"] }]
        :type tag_filters:  list of TagFilter
        :param offset:      偏移量，默认为0
        :type offset:       int
        :param limit:       返回数量，默认为50
        :type limit:        int
        :param order:       输出列表的排列顺序。取值范围：ASC：升序排列 DESC：降序排列
        :type order:        str
        :param order_field: 排序的依据字段， 取值范围 "CreateTime" "UpdateTime"
        :type order_field:  str
        :return:
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.DescribeBatchTasksResponse`
        """
        try:
            req = models.DescribeBatchTasksRequest()
            req.Filters = filters
            req.TagFilters = tag_filters
            req.Offset = offset
            req.Limit = limit
            req.Order = order
            req.OrderField = order_field
            return self._tione_client.DescribeBatchTasks(req)
        except TencentCloudSDKException as err:
            raise

    def stop_batch_task(self, batch_task_id):
        """停止跑批任务

        :param batch_task_id:
        :type batch_task_id: str
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.StopBatchTaskResponse`
        """
        try:
            req = models.StopBatchTaskRequest()
            req.BatchTaskId = batch_task_id
            return self._tione_client.StopBatchTask(req)
        except TencentCloudSDKException as err:
            raise

    def delete_batch_task(self, batch_task_id):
        """删除跑批任务

        :param batch_task_id:
        :type batch_task_id: str
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.DeleteBatchTaskResponse`
        """
        try:
            req = models.DeleteBatchTaskRequest()
            req.BatchTaskId = batch_task_id
            return self._tione_client.DeleteBatchTask(req)
        except TencentCloudSDKException as err:
            raise

    def _describe_model_accelerate_versions(self, filters=None, order_field=None, order=None, offset=0, limit=50):
        try:
            req = models.DescribeModelAccelerateVersionsRequest()
            req.Filters = filters
            req.OrderField = order_field
            req.order = order
            req.Offset = offset
            req.Limit = limit
            return self._tione_client.DescribeModelAccelerateVersions(req)
        except TencentCloudSDKException as err:
            raise

    def _new_name_filter(self, name, value):
        filter = models.Filter()
        filter.Name = name
        filter.Values = [value]
        filter.Fuzzy = False
        return [filter]

    def create_model_service_version(self,
                                     worker_resource,
                                     framework,
                                     service_group_id=None,
                                     service_description=None,
                                     resource_group_id=None,
                                     model_config_info=None,
                                     model_cos_path=None,
                                     volume_mount=None,
                                     service_limit=None,
                                     scheduled_action=None,
                                     model_hot_update_enable=False,
                                     env=None,
                                     scale_mode="MANUAL",
                                     replicas=1,
                                     horizontal_pod_autoscaler=None,
                                     log_enable=False,
                                     log_logset_id=None,
                                     log_topic_id=None,
                                     authorization_enable=False,
                                     tags=None,
                                     cron_scale_jobs=None,
                                     scale_strategy=None,
                                     hybrid_billing_prepaid_replicas=None):

        """创建模型服务版本

        :param worker_resource:        worker节点的配置
        :type worker_resource:         :class:`tikit.models.ModelServiceResourceConfigInfo`
        :param framework:              运行的框架环境
        :type framework:               :class:`tikit.models.FrameworkInfo`
        :param service_group_id:       服务id
        :type service_group_id:        str
        :param service_description:    服务描述
        :type service_description:     str
        :param resource_group_id:      预付费模式下所属的资源组id
        :type resource_group_id:       str
        :param model_config_info:      模型信息
        :type model_config_info:       :class:`tikit.models.ModelConfigInfo`
        :param model_cos_path:         模型cos信息
        :type model_cos_path:          str
        :param volume_mount:           挂载的存储信息
        :type volume_mount:            :class:`tikit.tencentcloud.tione.v20211111.models.VolumeMount`
        :param service_limit:          服务限流
        :type service_limit:           :class:`tikit.tencentcloud.tione.v20211111.models.ServiceLimit`
        :param scheduled_action:       定时停止的任务配置
        :type scheduled_action:        :class:`tikit.tencentcloud.tione.v20211111.models.ScheduledAction`
        :param model_hot_update_enable :是否开启模型的热更新。默认不开启
        :type model_hot_update_enable: bool
        :param env:                    环境变量
        :type env:                     list of :class:`tikit.tencentcloud.tione.v20211111.models.EnvVar`
        :param scale_mode:             扩缩容类型
        :type scale_mode:              str
        :param replicas:               实例数量
        :type replicas:                int
        :param horizontal_pod_autoscaler: 自动伸缩信息
        :type horizontal_pod_autoscaler:  :class:`tikit.tencentcloud.tione.v20211111.models.HorizontalPodAutoscaler`
        :param log_enable:             日志开关
        :type log_enable:              bool
        :param log_logset_id:          日志集id
        :type log_logset_id:           str
        :param log_topic_id:           日志的topic id
        :type log_topic_id:            str
        :param authorization_enable:   接口鉴权开关
        :type authorization_enable:    bool
        :param tags:                   标签
        :type tags:                    list of :class:`tikit.tencentcloud.tione.v20211111.models.Tag`
        :param cron_scale_jobs:        定时任务配置
        :type cron_scale_jobs:         list of :class:`tikit.tencentcloud.tione.v20211111.models.CronScaleJob`
        :param scale_strategy:         自动伸缩策略配置 HPA
        :type scale_strategy:          str
        :param hybrid_billing_prepaid_replicas: 混合计费模式下预付费实例数
        :type hybrid_billing_prepaid_replicas:  int
        :param model_hot_update_enable: 是否开启模型的热更新。默认不开启
        :type model_hot_update_enable: bool
        :return:
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.CreateModelServiceResponse`
        """

        return self._create_model_service_do("", worker_resource, framework, service_group_id,
                                            service_description, resource_group_id, model_config_info, model_cos_path,
                                            volume_mount, service_limit, scheduled_action, model_hot_update_enable,
                                            env, scale_mode, replicas, horizontal_pod_autoscaler, log_enable,
                                            log_logset_id, log_topic_id, authorization_enable, tags, cron_scale_jobs,
                                            scale_strategy, hybrid_billing_prepaid_replicas, True, model_hot_update_enable)

    def create_model_service(self,
                            service_group_name,
                            worker_resource,
                            framework=None,
                            service_description=None,
                            resource_group_id=None,
                            model_config_info=None,
                            model_cos_path=None,
                            volume_mount=None,
                            service_limit=None,
                            scheduled_action=None,
                            model_hot_update_enable=False,
                            env=None,
                            scale_mode="MANUAL",
                            replicas=1,
                            horizontal_pod_autoscaler=None,
                            log_enable=False,
                            log_logset_id=None,
                            log_topic_id=None,
                            authorization_enable=False,
                            tags=None,
                            cron_scale_jobs=None,
                            scale_strategy=None,
                            hybrid_billing_prepaid_replicas=None):

        """创建模型服务

        :param service_group_name:     服务名称
        :type service_group_name:      str
        :param worker_resource:        worker节点的配置
        :type worker_resource:         :class:`tikit.models.ModelServiceResourceConfigInfo`
        :param framework:              运行的框架环境
        :type framework:               :class:`tikit.models.FrameworkInfo`
        :param service_description:    服务描述
        :type service_description:     str
        :param resource_group_id:      预付费模式下所属的资源组id
        :type resource_group_id:       str
        :param model_config_info:      模型信息
        :type model_config_info:       :class:`tikit.models.ModelConfigInfo`
        :param model_cos_path:         模型cos信息
        :type model_cos_path:          str
        :param volume_mount:           挂载的存储信息
        :type volume_mount:            :class:`tikit.tencentcloud.tione.v20211111.models.VolumeMount`
        :param service_limit:          服务限流
        :type service_limit:           :class:`tikit.tencentcloud.tione.v20211111.models.ServiceLimit`
        :param scheduled_action:       定时停止的任务配置
        :type scheduled_action:        :class:`tikit.tencentcloud.tione.v20211111.models.ScheduledAction`
        :param model_hot_update_enable :是否开启模型的热更新。默认不开启
        :type model_hot_update_enable: bool
        :param env:                    环境变量
        :type env:                     list of :class:`tikit.tencentcloud.tione.v20211111.models.EnvVar`
        :param scale_mode:             扩缩容类型
        :type scale_mode:              str
        :param replicas:               实例数量
        :type replicas:                int
        :param horizontal_pod_autoscaler: 自动伸缩信息
        :type horizontal_pod_autoscaler:  :class:`tikit.tencentcloud.tione.v20211111.models.HorizontalPodAutoscaler`
        :param log_enable:             日志开关
        :type log_enable:              bool
        :param log_logset_id:          日志集id
        :type log_logset_id:           str
        :param log_topic_id:           日志的topic id
        :type log_topic_id:            str
        :param authorization_enable:   接口鉴权开关
        :type authorization_enable:    bool
        :param tags:                   标签
        :type tags:                    list of :class:`tikit.tencentcloud.tione.v20211111.models.Tag`
        :param cron_scale_jobs:        定时任务配置
        :type cron_scale_jobs:         list of :class:`tikit.tencentcloud.tione.v20211111.models.CronScaleJob`
        :param scale_strategy:         自动伸缩策略配置 HPA
        :type scale_strategy:          str
        :param hybrid_billing_prepaid_replicas: 混合计费模式下预付费实例数
        :type hybrid_billing_prepaid_replicas:  int
        :return:
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.CreateModelServiceResponse`
        """

        return self._create_model_service_do(service_group_name, worker_resource, framework, "",
                                            service_description, resource_group_id, model_config_info, model_cos_path,
                                            volume_mount, service_limit, scheduled_action, model_hot_update_enable,
                                            env, scale_mode, replicas, horizontal_pod_autoscaler, log_enable,
                                            log_logset_id, log_topic_id, authorization_enable, tags, cron_scale_jobs,
                                            scale_strategy, hybrid_billing_prepaid_replicas, False)

    def _create_model_service_from_file(self, filename: str):
        """从模型服务描述文件创建服务

        :param filename:     模型文件描述文件   
        :type filename:      str
        """
        args = self._create_model_service_file_to_arguments(filename)
        result = self._create_model_service_do(**args)
        if (result.Service and result.Service.ServiceGroupId and result.Service.ServiceId):
            service_group_id = result.Service.ServiceGroupId
            service_id = result.Service.ServiceId
            content = ''
            with open(filename, 'r') as infile:
                sgi_found = False
                si_found = False
                for line in infile:
                    if line.startswith('service_id'):
                        content += f'service_id: {service_id}\n'
                        si_found = True
                    elif line.startswith('service_group_id'):
                        content += f'service_group_id: {service_group_id}\n'
                        sgi_found = True
                    else:
                        content += line
                if not sgi_found:
                    content += f'service_group_id: {service_group_id}\n'
                if not si_found:
                    content += f'service_id: {service_id}\n'
            with open(filename, 'w') as outfile:
                outfile.write(content)
            return f'成功创建服务{service_id}，服务ID已经写入文件{filename}。如果需要创建新版本，需要新建一个配置文件，切勿重用文件{filename}，否则会丢失本次新建的服务配置。'

    def _create_model_service_file_to_arguments(self, filename: str)-> dict:
        result = {}
        if not os.path.exists(filename):
            raise FileNotFoundError(filename)
        import yaml
        with open(filename, 'r') as infile:
            data = yaml.load(infile, Loader=yaml.SafeLoader)

        if data.get('new_version'):
            service_group_id = data.get('service_group_id')
            if not service_group_id:
                raise ValueError('service_group_id should not be empty when you want to add a new version')
            result['new_version'] = True
            result['service_group_id'] = service_group_id
            result['service_group_name'] = data.get('service_group_name', '')
        else:
            if not data.get('service_group_name'):
                raise ValueError('service_group_name should not be empty')
            result['service_group_name'] = data['service_group_name']
            if data.get('service_group_description'):
                result['service_description'] = data['service_group_description']

        charge_type = data.get('charge_type')
        if charge_type == 'PREPAID':
            if not data.get('resource_group_id'):
                raise ValueError('resource_group_id should not be empty when charge_type is PREPAID')
            result['resource_group_id'] = data['resource_group_id']
            resources = data.get('resources')
            if not resources:
                raise ValueError('resources should not be empty when charge_type is PREPAID')
            result['worker_resource'] = TiModels.ModelServiceResourceConfigInfo.new_prepaid(
                resources.get('cpu') / 1000,
                resources.get('memory') / 1024,
                gpu=None if not resources.get('gpu') else resources['gpu'] / 100,
                gpu_type=None if not resources.get('gpu_type') else resources['gpu_type']
            )
        elif charge_type == 'POSTPAID_BY_HOUR':
            instance_type = data.get('instance_type')
            if not instance_type:
                raise ValueError('instance_type should not be empty when charge_type is POSTPAID_BY_HOUR')
            result['worker_resource'] = TiModels.ModelServiceResourceConfigInfo.new_postpaid(
                instance_type
            )
        elif charge_type == 'HYBRID_PAID':
            if not data.get('resource_group_id'):
                raise ValueError('resource_group_id should not be empty when charge_type is HYBRID_PAID')
            result['resource_group_id'] = data['resource_group_id']
            instance_type = data.get('instance_type')
            if not instance_type:
                raise ValueError('instance_type should not be empty when charge_type is HYBRID_PAID')
            result['worker_resource'] = TiModels.ModelServiceResourceConfigInfo.new_hybridpaid(instance_type)
            hybrid_billing_prepaid_replicas = data.get('hybrid_billing_prepaid_replicas')
            if not hybrid_billing_prepaid_replicas:
                raise ValueError('hybrid_billing_prepaid_replicas should not be empty when charge_type is HYBRID_PAID')
            result['hybrid_billing_prepaid_replicas'] = hybrid_billing_prepaid_replicas

        model_type = data.get('model_type', 'normal').upper()
        model_version_id = data.get('model_version_id')
        if model_version_id:
            result['model_config_info'] = TiModels.ModelConfigInfo.new_model_reference_lite(
                model_version_id, model_type
            )
        if not model_version_id and not data.get('image_info'):
            # 模型 与 镜像 必须要有一个
            raise ValueError('image_info should not be empty when no model chosed')
        if not model_version_id and data.get('image_info'):
            image_info = data['image_info']
            result['framework'] = TiModels.FrameworkInfo.new_custom_image(
                image_info.get('image_type'),
                image_info.get('image_url'),
                registry_region=None if not image_info.get('registry_region') else image_info['registry_region'],
                registry_id=None if not image_info.get('registry_id') else image_info['registry_id'],
            )
        if data.get('model_hot_update_enable'):
            result['model_hot_update_enable'] = True

        if data.get('env'):
            result['env'] = []
            for e in data.get('env'):
                ev = models.EnvVar()
                ev.Name = e['name'] 
                ev.Value = e['value']
                result['env'].append(ev)

        if data.get('replicas') is None:
            raise ValueError('replicas should not be empty')
        result['replicas'] = data['replicas']
        scale_mode = data.get('scale_mode', 'MANUAL')
        result['scale_mode'] = scale_mode
        scale_strategy = data.get('scale_strategy', None)
        if scale_strategy:
            result['scale_strategy'] = scale_strategy
        if scale_mode == 'AUTO' and scale_strategy == 'HPA':
            hpa = data.get('horizontal_pod_autoscaler')
            if not hpa:
                raise ValueError('horizontal_pod_autoscaler should not be empty')
            scaler = models.HorizontalPodAutoscaler()
            hpa_metrics = hpa.get('hpa_metrics')
            if not hpa_metrics:
                raise ValueError('hpa_metrics should not be empty')
            scaler.HpaMetrics = []
            for me in hpa_metrics:
                met = models.Option()
                met.Name = me.get('name')
                met.Value = me.get('value')
                scaler.HpaMetrics.append(met)
            scaler.MinReplicas = hpa.get('min_replicas', 1)
            scaler.MaxReplicas = hpa.get('max_replicas', 1)
            result['horizontal_pod_autoscaler'] = scaler
        elif scale_mode == 'AUTO' and scale_strategy == 'CRON':
            jobs = data.get('cron_scale_jobs')
            if not jobs:
                raise ValueError('cron_scale_jobs should not be empty')
            cron_jobs = []
            for j in jobs:
                job = models.CronScaleJob()
                job.Name = j.get('name')
                job.MinReplicas = j.get('min_replicas')
                job.MaxReplicas = j.get('max_replicas')
                job.TargetReplicas = j.get('target_replicas')
                job.Schedule = j.get('schedule')
                cron_jobs.append(job)
            result['cron_scale_jobs'] = cron_jobs
        elif scale_mode == 'MANUAL' and not scale_strategy:
            pass
        else:
            message = f'invalid value: (scale_mode x scale_strategy) tuple should only be one of [(MANUAL, NONE), (AUTO, HPA), (AUTO, CRON)], but your input is ({scale_mode}, {scale_strategy})'
            raise ValueError(message)
        
        if data.get('log_enable'):
            result['log_enable'] = True
            result['log_logset_id'] = data.get('log_config').get('logset_id')
            result['log_topic_id'] = data.get('log_config').get('topic_id')
        if data.get('authorization_enable'):
            result['authorization_enable'] = True
        if data.get('tags'):
            tags = []
            for t in data.get('tags'):
                tag = models.Tag()
                tag.TagKey = t.get('tag_key')
                tag.TagValue = t.get('tag_value')
                tags.append(tag)
            result['tags'] = tags
        _svc_limit = data.get('service_limit')
        if _svc_limit:
            service_limit = models.ServiceLimit()
            service_limit.EnableInstanceRpsLimit = _svc_limit.get('enable_instance_rps_limit', False)
            service_limit.InstanceRpsLimit = _svc_limit.get('instance_rps_limit', 0)
            result['service_limit'] = service_limit
        _vol_mount = data.get('volume_mount')
        if _vol_mount:
            volume_mount = models.VolumeMount()
            volume_mount.VolumeSourceType = _vol_mount.get('volume_source_type', 'CFS')
            cfs_config = _vol_mount.get('cfs_config')
            if not cfs_config:
                raise ValueError('cfs_config should not be empty')
            cfg = models.CFSConfig()
            cfg.Id = cfs_config.get('id')
            cfg.Path = cfs_config.get('path')
            volume_mount.CFSConfig = cfg
            result['volume_mount'] = volume_mount
        _sch_action = data.get('scheduled_action')
        if _sch_action:
            schedule_action = models.ScheduledAction()
            schedule_action.ScheduleStop = _sch_action.get('schedule_stop', False)
            stop_time = _sch_action.get('schedule_stop_time')
            if isinstance(stop_time, datetime.datetime):
                schedule_action.ScheduleStopTime = stop_time.astimezone().isoformat()
            else:
                schedule_action.ScheduleStopTime = stop_time
            result['scheduled_action'] = schedule_action
        return result

    def _create_model_service_do(self,
                                service_group_name,
                                worker_resource,
                                framework=None,
                                service_group_id=None,
                                service_description=None,
                                resource_group_id=None,
                                model_config_info=None,
                                model_cos_path=None,
                                volume_mount=None,
                                service_limit=None,
                                scheduled_action=None,
                                model_hot_update_enable=False,
                                env=None,
                                scale_mode="MANUAL",
                                replicas=1,
                                horizontal_pod_autoscaler=None,
                                log_enable=False,
                                log_logset_id=None,
                                log_topic_id=None,
                                authorization_enable=False,
                                tags=None,
                                cron_scale_jobs=None,
                                scale_strategy=None,
                                hybrid_billing_prepaid_replicas=None,
                                new_version=False):
        """创建模型服务

        :param service_group_name:     服务名称
        :type service_group_name:      str
        :param worker_resource:        worker节点的配置
        :type worker_resource:         :class:`tikit.models.ModelServiceResourceConfigInfo`
        :param framework:              运行的框架环境
        :type framework:               :class:`tikit.models.FrameworkInfo`
        :param service_group_id:       服务id
        :type service_group_id:        str
        :param service_description:    服务描述
        :type service_description:     str
        :param resource_group_id:      预付费模式下所属的资源组id
        :type resource_group_id:       str
        :param model_config_info:      模型信息
        :type model_config_info:       :class:`tikit.models.ModelConfigInfo`
        :param model_cos_path:         模型cos信息
        :type model_cos_path:          str
        :param volume_mount:           环境变量
        :type volume_mount:            :class:`tikit.tencentcloud.tione.v20211111.models.VolumeMount`
        :param service_limit:          服务限流
        :type service_limit:           :class:`tikit.tencentcloud.tione.v20211111.models.ServiceLimit`
        :param scheduled_action:       服务限流
        :type scheduled_action:        :class:`tikit.tencentcloud.tione.v20211111.models.ScheduledAction`
        :param model_hot_update_enable:是否开启模型的热更新。默认不开启
        :type model_hot_update_enable: bool
        :param env:                    环境变量
        :type env:                     list of :class:`tikit.tencentcloud.tione.v20211111.models.EnvVar`
        :param scale_mode:             扩缩容类型
        :type scale_mode:              str
        :param replicas:               实例数量
        :type replicas:                int
        :param horizontal_pod_autoscaler: 自动伸缩信息
        :type horizontal_pod_autoscaler:  :class:`tikit.tencentcloud.tione.v20211111.models.HorizontalPodAutoscaler`
        :param log_enable:             日志开关
        :type log_enable:              bool
        :param log_logset_id:          日志集id
        :type log_logset_id:           str
        :param log_topic_id:           日志的topic id
        :type log_topic_id:            str
        :param authorization_enable:   接口鉴权开关
        :type authorization_enable:    bool
        :param tags:                   标签
        :type tags:                    list of :class:`tikit.tencentcloud.tione.v20211111.models.Tag`
        :param cron_scale_jobs:        定时任务配置
        :type cron_scale_jobs:         list of :class:`tikit.tencentcloud.tione.v20211111.models.CronScaleJob`
        :param scale_strategy:         自动伸缩策略配置 HPA
        :type scale_strategy:          str
        :param hybrid_billing_prepaid_replicas: 混合计费模式下预付费实例数
        :type hybrid_billing_prepaid_replicas:  int
        :param new_version:            新建版本
        :type new_version:             bool
        :param model_hot_update_enable: 是否开启模型的热更新。默认不开启
        :type model_hot_update_enable: bool
        :return:
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.CreateModelServiceResponse`
        """

        try:
            req = models.CreateModelServiceRequest()
            req.ServiceGroupName = service_group_name
            charge_type = worker_resource.ChargeType
            req.ChargeType = charge_type

            if not framework:
                if not model_config_info or not model_config_info.ModelVersionId:
                    raise ValueError("framework should not be empty when no models chosen")
                modelInfo = self.describe_training_model_version(model_config_info.ModelVersionId)
                framework = TiModels.FrameworkInfo.new_custom_image(
                                                                    "PRESET",
                                                                    modelInfo.TrainingModelVersion.ReasoningEnvironment)
            if framework.Name == "CUSTOM":
                req.ImageInfo = models.ImageInfo()
                req.ImageInfo.ImageType = framework.ImageType
                req.ImageInfo.ImageUrl = framework.ImageUrl
                req.ImageInfo.RegistryRegion = framework.RegistryRegion
                req.ImageInfo.RegistryId = framework.RegistryId

            req.ServiceGroupId = service_group_id
            req.ServiceDescription = service_description
            if charge_type == "PREPAID" or charge_type == 'HYBRID_PAID':
                req.ResourceGroupId = resource_group_id

            if model_config_info is not None:
                req.ModelInfo = models.ModelInfo()
                req.ModelInfo.ModelId = model_config_info.ModelId
                req.ModelInfo.ModelName = model_config_info.ModelName
                req.ModelInfo.ModelVersionId = model_config_info.ModelVersionId
                req.ModelInfo.ModelVersion = model_config_info.ModelVersion
                req.ModelInfo.ModelSource = model_config_info.ModelSource
                # req.ModelInfo.CosPathInfo = self.parse_cos_info(model_cos_path) if model_cos_path else None
                if model_cos_path:
                    req.ModelInfo.CosPathInfo = self.parse_cos_info(model_cos_path)
                else:
                    req.ModelInfo.CosPathInfo = model_config_info.CosPathInfo
                req.ModelInfo.AlgorithmFramework = model_config_info.AlgorithmFramework
                req.ModelInfo.ModelType = model_config_info.ModelType

            req.Env = env
            
            req.VolumeMount = volume_mount
            req.ServiceLimit = service_limit
            req.ScheduledAction = scheduled_action
            req.ModelHotUpdateEnable = model_hot_update_enable

            worker_info = models.ResourceInfo()
            if charge_type == "PREPAID" or charge_type == "HYBRID_PAID":
                worker_info.Cpu = worker_resource.Cpu
                worker_info.Memory = worker_resource.Memory
                worker_info.Gpu = worker_resource.Gpu
                worker_info.GpuType = worker_resource.GpuType
            req.Resources = worker_info

            req.InstanceType = worker_resource.InstanceType
            req.ScaleMode = scale_mode
            req.Replicas = replicas
            req.HorizontalPodAutoscaler = horizontal_pod_autoscaler

            req.LogEnable = log_enable
            if log_enable:
                req.LogConfig = models.LogConfig()
                req.LogConfig.LogsetId = log_logset_id
                req.LogConfig.TopicId = log_topic_id
            req.AuthorizationEnable = authorization_enable
            req.Tags = tags
            req.CronScaleJobs = cron_scale_jobs
            req.ScaleStrategy = scale_strategy
            req.HybridBillingPrepaidReplicas = hybrid_billing_prepaid_replicas
            req.NewVersion = new_version

            print(req._serialize())
            return self._tione_client.CreateModelService(req)
        except TencentCloudSDKException as err:
            raise

    def _modify_model_service_from_file(self, filename: str):
        """从模型服务描述文件更改服务

        :param filename:     模型文件描述文件   
        :type filename:      str
        """
        args = self._modify_model_service_file_to_arguments(filename)
        result = self.modify_model_service(**args)
        if (result.Service and result.Service.ServiceGroupId and
               result.Service.ServiceId):
            return f'success to update ModelService {result.Service.ServiceId}'
    
    def _modify_model_service_file_to_arguments(self, filename: str)-> dict:
        result = {}
        if not os.path.exists(filename):
            raise FileNotFoundError(filename)
        import yaml
        with open(filename, 'r') as infile:
            data = yaml.load(infile, Loader=yaml.SafeLoader)

        service_id = data.get('service_id')
        if not service_id:
            raise ValueError('service_id should not be empty when you want to add a new version')
        result['service_id'] = service_id

        if data.get('service_group_description'):
            result['service_description'] = data['service_group_description']

        model_type = data.get('model_type', 'normal').upper()
        model_version_id = data.get('model_version_id')
        if data.get('model_version_id'):
            result['model_config_info'] = TiModels.ModelConfigInfo.new_model_reference_lite(
                model_version_id, model_type,
            )
        if not model_version_id and not data.get('image_info'):
            # 模型 与 镜像 必须要有一个
            raise ValueError('image_info should not be empty when no model chosed')
        if not model_version_id and data.get('image_info'):
            image_info = data['image_info']
            result['framework'] = TiModels.FrameworkInfo.new_custom_image(
                image_info.get('image_type'),
                image_info.get('image_url'),
                registry_region=None if not image_info.get('registry_region') else image_info['registry_region'],
                registry_id=None if not image_info.get('registry_id') else image_info['registry_id'],
            )
        if data.get('model_hot_update_enable'):
            result['model_hot_update_enable'] = True
        
        if data.get('env'):
            result['env'] = []
            for e in data.get('env'):
                ev = models.EnvVar()
                ev.Name = e['name'] 
                ev.Value = e['value']
                result['env'].append(ev)

        if data.get('replicas') is None:
            raise ValueError('replicas should not be empty')
        result['replicas'] = data['replicas']
        hybrid_billing_prepaid_replicas = data.get('hybrid_billing_prepaid_replicas')
        if hybrid_billing_prepaid_replicas:
            result['hybrid_billing_prepaid_replicas'] = hybrid_billing_prepaid_replicas
        scale_mode = data.get('scale_mode', 'MANUAL')
        result['scale_mode'] = scale_mode
        scale_strategy = data.get('scale_strategy', None)
        if scale_strategy:
            result['scale_strategy'] = scale_strategy
        if scale_mode == 'AUTO' and scale_strategy == 'HPA':
            hpa = data.get('horizontal_pod_autoscaler')
            if not hpa:
                raise ValueError('horizontal_pod_autoscaler should not be empty')
            scaler = models.HorizontalPodAutoscaler()
            hpa_metrics = hpa.get('hpa_metrics')
            if not hpa_metrics:
                raise ValueError('hpa_metrics should not be empty')
            scaler.HpaMetrics = []
            for me in hpa_metrics:
                met = models.Option()
                met.Name = me.get('name')
                met.Value = me.get('value')
                scaler.HpaMetrics.append(met)
            scaler.MinReplicas = hpa.get('min_replicas', 1)
            scaler.MaxReplicas = hpa.get('max_replicas', 1)
            result['horizontal_pod_autoscaler'] = scaler
        elif scale_mode == 'AUTO' and scale_strategy == 'CRON':
            jobs = data.get('cron_scale_jobs')
            if not jobs:
                raise ValueError('cron_scale_jobs should not be empty')
            cron_jobs = []
            for j in jobs:
                job = models.CronScaleJob()
                job.Name = j.get('name')
                job.MinReplicas = j.get('min_replicas')
                job.MaxReplicas = j.get('max_replicas')
                job.TargetReplicas = j.get('target_replicas')
                job.Schedule = j.get('schedule')
                cron_jobs.append(job)
            result['cron_scale_jobs'] = cron_jobs
        elif scale_mode == 'MANUAL' and not scale_strategy:
            pass
        else:
            message = f'invalid value: (scale_mode x scale_strategy) tuple should only be one of [(MANUAL, NONE), (AUTO, HPA), (AUTO, CRON)], but your input is ({scale_mode}, {scale_strategy})'
            raise ValueError(message)
        
        if data.get('log_enable'):
            result['log_enable'] = True
            result['log_logset_id'] = data.get('log_config').get('logset_id')
            result['log_topic_id'] = data.get('log_config').get('topic_id')
        if data.get('authorization_enable'):
            result['authorization_enable'] = True
        _svc_limit = data.get('service_limit')
        if _svc_limit:
            service_limit = models.ServiceLimit()
            service_limit.EnableInstanceRpsLimit = _svc_limit.get('enable_instance_rps_limit', False)
            service_limit.InstanceRpsLimit = _svc_limit.get('instance_rps_limit', 0)
            result['service_limit'] = service_limit
        _vol_mount = data.get('volume_mount')
        if _vol_mount:
            volume_mount = models.VolumeMount()
            volume_mount.VolumeSourceType = _vol_mount.get('volume_source_type', 'CFS')
            cfs_config = _vol_mount.get('cfs_config')
            if not cfs_config:
                raise ValueError('cfs_config should not be empty')
            cfg = models.CFSConfig()
            cfg.Id = cfs_config.get('id')
            cfg.Path = cfs_config.get('path')
            volume_mount.CFSConfig = cfg
            result['volume_mount'] = volume_mount
        _sch_action = data.get('scheduled_action')
        if _sch_action:
            schedule_action = models.ScheduledAction()
            schedule_action.ScheduleStop = _sch_action.get('schedule_stop', False)
            stop_time = _sch_action.get('schedule_stop_time')
            if isinstance(stop_time, datetime.datetime):
                schedule_action.ScheduleStopTime = stop_time.astimezone().isoformat()
            else:
                schedule_action.ScheduleStopTime = stop_time
            result['scheduled_action'] = schedule_action
        return result


    def modify_model_service(self,
                             service_id,
                             model_config_info=None,
                             model_cos_path=None,
                             framework=None,
                             volume_mount=None,
                             service_limit=None,
                             scheduled_action=None,
                             model_hot_update_enable=None,
                             env=None,
                             worker_resource=None,
                             scale_mode="MANUAL",
                             replicas=1,
                             horizontal_pod_autoscaler=None,
                             log_enable=False,
                             log_logset_id=None,
                             log_topic_id=None,
                             service_action=None,
                             service_description=None,
                             scale_strategy=None,
                             cron_scale_jobs=None,
                             hybrid_billing_prepaid_replicas=None,
                             )->models.ModifyModelServiceResponse:
        """更新模型服务版本

        :param service_id:                 服务版本id
        :type service_id:                  str
        :param model_config_info:          模型信息
        :type model_config_info:           :class:`tikit.models.ModelConfigInfo`
        :param model_cos_path:             模型cos信息
        :type model_cos_path:              str
        :param framework:                  运行的框架环境
        :type framework:                   :class:`tikit.models.FrameworkInfo`
        :param volume_mount:               挂载的存储信息
        :type volume_mount:                :class:`tikit.tencentcloud.tione.v20211111.models.VolumeMount`
        :param service_limit:              服务限流
        :type service_limit:               :class:`tikit.tencentcloud.tione.v20211111.models.ServiceLimit`
        :param scheduled_action:           定时停止的任务配置
        :type scheduled_action:            :class:`tikit.tencentcloud.tione.v20211111.models.ScheduledAction`
        :param model_hot_update_enable     :是否开启模型的热更新。默认不开启
        :type model_hot_update_enable:     bool
        :param env:                        环境变量
        :type env:                         list of :class:`tikit.tencentcloud.tione.v20211111.models.EnvVar`
        :param worker_resource:            worker节点配置
        :type worker_resource:             :class:`tikit.models.ModelServiceResourceConfigInfo`
        :param scale_mode:                 扩缩容类型
        :type scale_mode:                  str
        :param replicas:                   实例数量
        :type replicas:                    int
        :param horizontal_pod_autoscaler:  自动伸缩信息
        :type horizontal_pod_autoscaler:   :class:`tikit.tencentcloud.tione.v20211111.models.HorizontalPodAutoscaler`
        :param log_enable:                 日志开关
        :type log_enable:                  bool
        :param log_logset_id:              日志id
        :type log_logset_id:               str
        :param log_topic_id:               日志topic id
        :type log_topic_id:                str
        :param service_action:             特殊更新行为
        :type service_action:              str
        :param service_description:        服务描述
        :type service_description:         str
        :param scale_strategy:             自动伸缩策略
        :type scale_strategy:              str
        :param cron_scale_jobs:            自动伸缩策略配置 HPA
        :type cron_scale_jobs:             list of :class:`tikit.tencentcloud.tione.v20211111.models.CronScaleJob`
        :param hybrid_billing_prepaid_replicas:  混合计费模式下预付费实例数
        :type hybrid_billing_prepaid_replicas:   int
        :return:
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.ModifyModelServiceResponse`
        """

        try:            
            req = models.ModifyModelServiceRequest()
            req.ServiceId = service_id

            if model_config_info is not None:
                req.ModelInfo = models.ModelInfo()
                req.ModelInfo.ModelId = model_config_info.ModelId
                req.ModelInfo.ModelName = model_config_info.ModelName
                req.ModelInfo.ModelVersionId = model_config_info.ModelVersionId
                req.ModelInfo.ModelVersion = model_config_info.ModelVersion
                req.ModelInfo.ModelSource = model_config_info.ModelSource
                # req.ModelInfo.CosPathInfo = self.parse_cos_info(model_cos_path) if model_cos_path else None
                if model_cos_path:
                    req.ModelInfo.CosPathInfo = self.parse_cos_info(model_cos_path)
                else:
                    req.ModelInfo.CosPathInfo = model_config_info.CosPathInfo
                req.ModelInfo.AlgorithmFramework = model_config_info.AlgorithmFramework
                req.ModelInfo.ModelType = model_config_info.ModelType

            if framework and framework.Name == "CUSTOM":
                req.ImageInfo = models.ImageInfo()
                req.ImageInfo.ImageType = framework.ImageType
                req.ImageInfo.ImageUrl = framework.ImageUrl
                req.ImageInfo.RegistryRegion = framework.RegistryRegion
                req.ImageInfo.RegistryId = framework.RegistryId

            req.Env = env
            
            req.VolumeMount = volume_mount
            req.ServiceLimit = service_limit
            req.ScheduledAction = scheduled_action
            req.ModelHotUpdateEnable = model_hot_update_enable

            worker_info = models.ResourceInfo()
            if worker_resource:
                if worker_resource.ChargeType == "PREPAID":
                    worker_info.Cpu = worker_resource.Cpu
                    worker_info.Memory = worker_resource.Memory
                    worker_info.Gpu = worker_resource.Gpu
                    worker_info.GpuType = worker_resource.GpuType
                else:
                    req.InstanceType = worker_resource.InstanceType
            req.Resources = worker_info

            req.ScaleMode = scale_mode
            req.Replicas = replicas
            req.HorizontalPodAutoscaler = horizontal_pod_autoscaler
            req.LogEnable = log_enable
            if log_enable:
                req.LogConfig = models.LogConfig()
                req.LogConfig.LogsetId = log_logset_id
                req.LogConfig.TopicId = log_topic_id

            req.ServiceAction = service_action
            req.ServiceDescription = service_description
            req.ScaleStrategy = scale_strategy
            req.CronScaleJobs = cron_scale_jobs
            req.HybridBillingPrepaidReplicas = hybrid_billing_prepaid_replicas

            print(req._serialize())
            return self._tione_client.ModifyModelService(req)
        except TencentCloudSDKException as err:
            raise
        
    def patch_model_service(self,
                            service_id,
                            model_config_info=None,
                            model_cos_path=None,
                            framework=None,
                            volume_mount=None,
                            service_limit=None,
                            scheduled_action=None,
                            model_hot_update_enable=None,
                            env=None,
                            worker_resource=None,
                            scale_mode=None,
                            replicas=None,
                            horizontal_pod_autoscaler=None,
                            log_enable=False,
                            log_logset_id=None,
                            log_topic_id=None,
                            service_action=None,
                            service_description=None,
                            scale_strategy=None,
                            cron_scale_jobs=None):
        """部分更新模型服务版本，不传的参数默认不更新

        :param service_id:                 服务版本id
        :type service_id:                  str
        :param model_config_info:          模型信息
        :type model_config_info:           :class:`tikit.models.ModelConfigInfo`
        :param model_cos_path:             模型cos信息
        :type model_cos_path:              str
        :param framework:                  运行的框架环境
        :type framework:                   :class:`tikit.models.FrameworkInfo`
        :param volume_mount:               挂载的存储信息
        :type volume_mount:                :class:`tikit.tencentcloud.tione.v20211111.models.VolumeMount`
        :param service_limit:              服务限流
        :type service_limit:               :class:`tikit.tencentcloud.tione.v20211111.models.ServiceLimit`
        :param scheduled_action:           定时停止的任务配置
        :type scheduled_action:            :class:`tikit.tencentcloud.tione.v20211111.models.ScheduledAction`
        :param model_hot_update_enable     :是否开启模型的热更新。默认不开启
        :type model_hot_update_enable:     bool
        :param env:                        环境变量
        :type env:                         list of :class:`tikit.tencentcloud.tione.v20211111.models.EnvVar`
        :param worker_resource:            worker节点配置
        :type worker_resource:             :class:`tikit.models.ModelServiceResourceConfigInfo`
        :param scale_mode:                 扩缩容类型
        :type scale_mode:                  str
        :param replicas:                   实例数量
        :type replicas:                    int
        :param horizontal_pod_autoscaler:  自动伸缩信息
        :type horizontal_pod_autoscaler:   :class:`tikit.tencentcloud.tione.v20211111.models.HorizontalPodAutoscaler`
        :param log_enable:                 日志开关
        :type log_enable:                  bool
        :param log_logset_id:              日志id
        :type log_logset_id:               str
        :param log_topic_id:               日志topic id
        :type log_topic_id:                str
        :param service_action:             特殊更新行为
        :type service_action:              str
        :param service_description:        服务版本描述
        :type service_description:         str
        :param scale_strategy:             自动伸缩策略
        :type scale_strategy:              str
        :param cron_scale_jobs:            自动伸缩策略配置 HPA
        :type cron_scale_jobs:             list of :class:`tikit.tencentcloud.tione.v20211111.models.CronScaleJob`
        :return:
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.ModifyModelServiceResponse`
        """
        
        

        try:
            query_req = models.DescribeModelServiceRequest()
            query_req.ServiceId = service_id
            query_rsp = self._tione_client.DescribeModelService(query_req)
            if isinstance(query_rsp, TencentCloudSDKException):
                # error
                raise
            oldService = query_rsp.Service
            
            req = models.ModifyModelServiceRequest()
            req.ServiceId = service_id

            if model_config_info is not None:
                req.ModelInfo = models.ModelInfo()
                req.ModelInfo.ModelId = model_config_info.ModelId
                req.ModelInfo.ModelName = model_config_info.ModelName
                req.ModelInfo.ModelVersionId = model_config_info.ModelVersionId
                req.ModelInfo.ModelVersion = model_config_info.ModelVersion
                req.ModelInfo.ModelSource = model_config_info.ModelSource
                # req.ModelInfo.CosPathInfo = self.parse_cos_info(model_cos_path) if model_cos_path else None
                if model_cos_path:
                    req.ModelInfo.CosPathInfo = self.parse_cos_info(model_cos_path)
                else:
                    req.ModelInfo.CosPathInfo = model_config_info.CosPathInfo
                req.ModelInfo.AlgorithmFramework = model_config_info.AlgorithmFramework
                req.ModelInfo.ModelType = model_config_info.ModelType
            else:
                req.ModelInfo = oldService.ServiceInfo.ModelInfo

            if framework and framework.Name == "CUSTOM":
                req.ImageInfo = models.ImageInfo()
                req.ImageInfo.ImageType = framework.ImageType
                req.ImageInfo.ImageUrl = framework.ImageUrl
                req.ImageInfo.RegistryRegion = framework.RegistryRegion
                req.ImageInfo.RegistryId = framework.RegistryId
            else:
                req.ImageInfo = oldService.ServiceInfo.ImageInfo

            
            if env is not None:
                req.Env = env
            else:
                req.Env = oldService.ServiceInfo.Env
            
            if volume_mount is not None:
                req.VolumeMount = volume_mount
            else:
                req.VolumeMount = oldService.ServiceInfo.VolumeMount
                
            if service_limit is not None:
                req.ServiceLimit = service_limit
            else:
                req.ServiceLimit = oldService.ServiceLimit
                
            if scheduled_action is not None:
                req.ScheduledAction = scheduled_action
            else:
                req.ScheduledAction = oldService.ScheduledAction
                
            if model_hot_update_enable is not None:
                req.ModelHotUpdateEnable = model_hot_update_enable
            else:
                req.ModelHotUpdateEnable = oldService.ServiceInfo.ModelHotUpdateEnable
            
            if oldService.ChargeType == "PREPAID":
                worker_info = models.ResourceInfo()
                if worker_resource is not None:
                    worker_info.Cpu = worker_resource.Cpu
                    worker_info.Memory = worker_resource.Memory
                    worker_info.Gpu = worker_resource.Gpu
                    worker_info.GpuType = worker_resource.GpuType
                else:
                    worker_info = oldService.ServiceInfo.Resources
                req.Resources = worker_info
            else:
                if worker_resource is not None:
                    req.InstanceType = worker_resource.InstanceType
                else:
                    req.InstanceType = oldService.ServiceInfo.InstanceType       

            if req.ScaleMode is not None:
                req.ScaleMode = scale_mode
            else:
                req.ScaleMode = oldService.ServiceInfo.ScaleMode
            
            if req.Replicas is not None:
                req.Replicas = replicas
            else:
                req.Replicas = oldService.ServiceInfo.Replicas
                
            if horizontal_pod_autoscaler is not None:
                req.HorizontalPodAutoscaler = horizontal_pod_autoscaler
            else:
                req.HorizontalPodAutoscaler = oldService.ServiceInfo.HorizontalPodAutoscaler
            
            if log_enable is not None:
                req.LogEnable = log_enable
                if log_enable:
                    req.LogConfig = models.LogConfig()
                req.LogConfig.LogsetId = log_logset_id
                req.LogConfig.TopicId = log_topic_id
            else:
                req.LogEnable = oldService.ServiceInfo.LogEnable
                if log_enable:
                    req.LogConfig = models.LogConfig()
                req.LogConfig.LogsetId = oldService.ServiceInfo.LogConfig.LogsetId
                req.LogConfig.TopicId = oldService.ServiceInfo.LogConfig.TopicId
                
            req.ServiceAction = service_action
            if service_description is not None:
                req.ServiceDescription = service_description
            else:
                req.ServiceDescription = oldService.ServiceDescription
            
            if scale_strategy is not None:
                req.ScaleStrategy = scale_strategy
            else:
                req.ScaleStrategy = oldService.ServiceInfo.ScaleStrategy
                
            if cron_scale_jobs is not None:
                req.CronScaleJobs = cron_scale_jobs
            else:
                req.CronScaleJobs = oldService.ServiceInfo.CronScaleJobs

            print(req._serialize())
            return self._tione_client.ModifyModelService(req)
        except TencentCloudSDKException as err:
            raise
    
    def scale_model_service(self,
                            service_id,
                            scale_mode="MANUAL",
                            replicas=1,
                            horizontal_pod_autoscaler=None,
                            scale_strategy=None,
                            cron_scale_jobs=None):
        """模型服务版本扩缩容

        :param service_id:                 服务版本id
        :type service_id:                  str
        :param scale_mode:                 扩缩容类型
        :type scale_mode:                  str
        :param replicas:                   实例数量
        :type replicas:                    int
        :param horizontal_pod_autoscaler:  自动伸缩信息
        :type horizontal_pod_autoscaler:   :class:`tikit.tencentcloud.tione.v20211111.models.HorizontalPodAutoscaler`
        :param scale_strategy:             自动伸缩策略
        :type scale_strategy:              str
        :param cron_scale_jobs:            自动伸缩策略配置 HPA
        :type cron_scale_jobs:             list of :class:`tikit.tencentcloud.tione.v20211111.models.CronScaleJob`
        :return:
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.ModifyModelServiceResponse`
        """

        try:
            req = models.ModifyModelServiceRequest()
            req.ServiceId = service_id
            req.ScaleMode = scale_mode
            req.Replicas = replicas
            req.HorizontalPodAutoscaler = horizontal_pod_autoscaler
            req.ServiceAction = "SCALE"
            req.ScaleStrategy = scale_strategy
            req.CronScaleJobs = cron_scale_jobs
            print(req._serialize())
            return self._tione_client.ModifyModelService(req)
        except TencentCloudSDKException as err:
            raise
    
    
    
    def describe_api_configs(self, service_group_id: str) -> models.DescribeAPIConfigsResponse:
        """查看服务对应的API

        :param service_group_id: 服务id
        :type service_group_id: str
        :return:
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.DescribeAPIConfigsResponse`
        """
        req = models.DescribeAPIConfigsRequest()
        f = models.Filter()
        f.Name = 'ServiceGroupId'
        f.Values = [service_group_id]
        req.Filters = [f]
        try: 
            return self._tione_client.DescribeAPIConfigs(req)
        except TencentCloudSDKException as err:
            raise

    def describe_service_call_info(self, service_group_id: str) -> models.DescribeModelServiceCallInfoResponse:
        """查看服务对应的调用信息

        :param service_group_id: 服务id
        :type service_group_id: str
        :return:
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.DescribeModelServiceCallInfoResponse`
        """
        req = models.DescribeModelServiceCallInfoRequest()
        req.ServiceGroupId = service_group_id
        try: 
            return self._tione_client.DescribeModelServiceCallInfo(req)
        except TencentCloudSDKException as err:
            raise
    
    
    def get_model_service_summary(self):
        """查询所有服务的概要

        :return:
        :rtype: list
        """
        resp = self.describe_model_service_groups()
        if not resp.ServiceGroups:
            return None
        svcs = []
        for group in resp.ServiceGroups:
            group_id = group.ServiceGroupId
            group_name = group.ServiceGroupName
            for svc in group.Services:
                svc_id = svc.ServiceId
                svc_desc = svc.ServiceDescription
                charge_type = svc.ChargeType
                svcs.append({
                    'Name': group_name,
                    'ServiceGroupId': group_id,
                    'ServiceId': svc_id,
                    'Description': svc_desc,
                    'ChargeType': charge_type,
                })
        return svcs

    def describe_model_service_groups(self, filters=None, order_field=None, order=None, offset=None, limit=None,
                                 tag_filters=None):
        """查看所有在线推理服务

        :param filters: 过滤器
        :type filters: list of Filter
        :param order_field: 排序字段
        :type order_field: str
        :param order: 排序方式，ASC/DESC
        :type order: str
        :param offset: 偏移量
        :type offset: int
        :param limit: 返回结果数量
        :type limit: int
        :param tag_filters: 标签过滤
        :type tag_filters: list of TagFilter
        :return:
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.DescribeModelServiceGroupsResponse`
        """
        try:
            req = models.DescribeModelServiceGroupsRequest()
            req.Filters = filters
            req.OrderField = order_field
            req.Order = order
            req.Offset = offset
            req.Limit = limit
            req.TagFilters = tag_filters
            return self._tione_client.DescribeModelServiceGroups(req)
        except TencentCloudSDKException as err:
            raise

    def describe_model_service_group(self, service_group_id):
        """查寻单个服务

        :param service_group_id: 服务id
        :type service_group_id: str
        :return:
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.DescribeModelServiceGroupResponse`
        """
        try:
            req = models.DescribeModelServiceGroupRequest()
            req.ServiceGroupId = service_group_id
            return self._tione_client.DescribeModelServiceGroup(req)
        except TencentCloudSDKException as err:
            raise

    def describe_model_services(self, filters=None, order_field=None, order=None, offset=None, limit=None,
                                 tag_filters=None):
        """查询多个服务版本

        :param filters: 过滤器
        :type filters: list of Filter
        :param order_field: 排序字段
        :type order_field: str
        :param order: 排序方式，ASC/DESC
        :type order: str
        :param offset: 偏移量
        :type offset: int
        :param limit: 返回结果数量
        :type limit: int
        :param tag_filters: 标签过滤
        :type tag_filters: list of TagFilter
        :return:
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.DescribeModelServicesResponse`
        """
        try:
            req = models.DescribeModelServicesRequest()
            req.Filters = filters
            req.OrderField = order_field
            req.Order = order
            req.Offset = offset
            req.Limit = limit
            req.TagFilters = tag_filters
            return self._tione_client.DescribeModelServices(req)
        except TencentCloudSDKException as err:
            raise

    def describe_model_service(self, service_id):
        """查询单个服务版本

        :param service_id: 服务版本id
        :type service_id: str
        :return:
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.DescribeModelServiceResponse`
        """
        try:
            req = models.DescribeModelServiceRequest()
            req.ServiceId = service_id
            return self._tione_client.DescribeModelService(req)
        except TencentCloudSDKException as err:
            raise

    def stop_model_service(self, service_id:str):
        """关闭服务版本

        :param service_id: 服务版本id
        :type service_id: str
        :return:
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.ModifyModelServiceResponse`
        """
        try:
            return self.modify_model_service(service_id=service_id, service_action='STOP')
        except TencentCloudSDKException as err:
            raise

    def start_model_service(self, service_id):
        """启动已经关闭的服务版本

        :param service_id: 服务版本id
        :type service_id: str
        :return:
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.ModifyModelServiceResponse`
        """
        try:
            return self.modify_model_service(service_id=service_id, service_action='RESUME')
        except TencentCloudSDKException as err:
            raise

    def delete_model_service(self, service_id):
        """删除已经关闭的服务版本

        :param service_id: 服务版本id
        :type service_id: str
        :return:
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.DeleteModelServiceResponse`
        """
        try:
            req = models.DeleteModelServiceRequest()
            req.ServiceId = service_id
            return self._tione_client.DeleteModelService(req)
        except TencentCloudSDKException as err:
            raise

    def delete_model_service_group(self, service_group_id):
        """删除服务

        :param service_group_id: 服务id
        :type service_group_id: str
        :return:
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.DeleteModelServiceGroupResponse`
        """
        try:
            req = models.DeleteModelServiceGroupRequest()
            req.ServiceGroupId = service_group_id
            return self._tione_client.DeleteModelServiceGroup(req)
        except TencentCloudSDKException as err:
            raise

    def modify_service_group_weights(self, service_group_id, service_id_to_weight):
        """更改服务的权重分配

        :param service_group_id: 服务id
        :type service_group_id: str
        :param service_id_to_weight: 服务版本id与权重对
        :type service_id_to_weight: dict
        :return:
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.ModifyServiceGroupWeightsResponse`
        """
        try:
            req = models.ModifyServiceGroupWeightsRequest()
            req.ServiceGroupId = service_group_id
            req.Weights = []
            for service_id, weight in service_id_to_weight.items():
                entry = models.WeightEntry()
                entry.ServiceId = service_id
                entry.Weight = weight
                req.Weights.append(entry)
            return self._tione_client.ModifyServiceGroupWeights(req)
        except TencentCloudSDKException as err:
            raise

    def get_service_group_id_by_name(self, name: str)->str:
        """根据服务名字查询服务ID

        :param name: 服务名字
        :type order: str
        :return:
        :rtype: str
        """
        if not name:
            raise ValueError('name should not be empty')
        f = models.Filter()
        f.Name = 'ServiceGroupName'
        f.Values = [name]
        resp = self.describe_model_service_groups(filters=[f])
        if len(resp.ServiceGroups) > 1:
            raise ValueError(f'name should be unique, but {len(resp.ServiceGroups)} service groups found')
        if len(resp.ServiceGroups) == 1:
            return resp.ServiceGroups[0].ServiceGroupId
        return name
    
    def describe_infer_templates(self)->models.DescribeInferTemplatesResponse:
        """查询推理框架信息

        :return:
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.DescribeInferTemplatesResponse`
        """
        try:
            req = models.DescribeInferTemplatesRequest()
            return self._tione_client.DescribeInferTemplates(req)
        except TencentCloudSDKException as err:
            raise
    
    def describe_reasoning_env(self)->models.DescribeInferTemplatesResponse:
        """查询推理环境信息

        :return:
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.DescribeInferTemplatesResponse`
        """
        return self.describe_infer_templates()
    
    def get_service_id_by_name(self, name: str, version: str)->str:
        """根据服务版本名字查询服务版本ID

        :param name: 服务版本名字
        :type order: str
        :return:
        :rtype: str
        """
        if not name:
            raise ValueError('name should not be empty')
        if not version:
            raise ValueError('version should not be empty')
        version = version.lstrip('vV')
        f = models.Filter()
        f.Name = 'ServiceGroupName'
        f.Values = [name]
        resp = self.describe_model_services(filters=[f])
        if not resp.Services or len(resp.Services) == 0:
            raise ValueError(f'no service found for {name}:{version})')
        service_id = ''
        for svc in resp.Services:
            if svc.Version == version:
                service_id = svc.ServiceId
                break
        if not service_id:
            raise ValueError(f'no service found for {name}:{version})')
        return service_id

    def start_training_task(self, task_id):
        """启动某个训练任务

        :param task_id: 训练任务ID
        :type task_id: str
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.StopTrainingTaskResponse`
        """
        try:
            req = models.StopTrainingTaskRequest()
            req.Id = task_id
            return self._tione_client.StopTrainingTask(req)
        except TencentCloudSDKException as err:
            raise

    def get_client_info(self):
        return {
            "secret_id": self._secret_id,
            "secret_key": self._secret_key,
            "region": self._region,
            "tione_client": self._tione_client
        }

    
    def describe_model_accelerate_tasks(self, filters=None, order_field=None, order=None, offset=None, limit=None,
                                 tag_filters=None):
        """查看加速任务列表

        :param filters:         过滤器
        :type filters:          list of Filter
        :param order_field:     排序字段
        :type order_field:      str
        :param order:           排序方式，ASC/DESC
        :type order:            str
        :param offset:          偏移量
        :type offset:           int
        :param limit:           返回结果数量
        :type limit:            int
        :param tag_filters:     标签过滤
        :type tag_filters:      list of TagFilter
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.DescribeModelAccelerateTasksResponse`

        """
        try:
            req = models.DescribeModelAccelerateTasksRequest()
            req.Filters = filters
            req.OrderField = order_field
            req.Order = order
            req.Offset = offset
            req.Limit = limit
            req.TagFilters = tag_filters
            return self._tione_client.DescribeModelAccelerateTasks(req)
        except TencentCloudSDKException as err:
            raise

    
    def create_model_accelerate_task(self,
                             model_acc_task_name,
                             model_source,
                             model_format,
                             model_name,
                             model_version,
                             model_input_path,
                             model_output_path,
                             tensor_infos,
                             acc_engine_version = None,
                             optimization_level = None,
                             gpu_type = None,
                             hyper_parameter = None,
                             tags = None,
                             model_signature = None,
                             framework_version = None):
        """创建模型加速任务

        :param model_acc_task_name:        任务名称
        :type model_acc_task_name:         str
        :param model_source:               模型来源(JOB/COS)
        :type model_source:                str
        :param model_format:               模型格式(TORCH_SCRIPT/DETECTRON2/SAVED_MODEL/FROZEN_GRAPH/MMDETECTION/ONNX/HUGGING_FACE)
        :type model_format:                str
        :param model_name:                 模型名称
        :type model_name:                  str
        :param model_version:              模型版本
        :type model_version:               str
        :param model_input_path:           模型输入cos地址，需要是具体的模型文件路径，eg"tione-test/example/model/checkpoint.pth"
        :type model_input_path:            :class:`tikit.models.CosPathInfo`
        :param model_output_path:          优化模型输出cos地址，需要是保存模型文件的路径，是一个文件夹，以"/"结尾 eg:"tione-test/example/output/"
        :type model_output_path:           :class:`tikit.models.CosPathInfo`
        :param acc_engine_version:         优化引擎版本，不传则默认使用最新版本
        :type acc_engine_version:          str
        :param tensor_infos:               tensor信息
        :type tensor_infos:                list of str
        :param optimization_level:         优化级别(NO_LOSS/FP16/INT8),默认FP16
        :type optimization_level:          str  
        :param gpu_type:                   GPU卡类型(T4/V100),默认T4
        :type gpu_type:                    str
        :param hyper_parameter:            专业参数设置
        :type hyper_parameter:             :class:`tikit.tencentcloud.tione.v20211111.models.HyperParameter`
        :param tags:                       标签
        :type tags:                        list of :class:`tikit.tencentcloud.tione.v20211111.models.Tag`
        :param model_signature:            SAVED_MODEL保存时配置的签名
        :type model_signature:             str
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.CreateModelAccelerateTaskResponse` 
        """
        try:
            req = models.CreateModelAccelerateTaskRequest()
            req.ModelAccTaskName = model_acc_task_name
            req.ModelSource = model_source
            self._set_cos_input_path_info(req, model_input_path)
            self._set_cos_path_info(req, model_output_path)
            req.ModelName = model_name
            req.ModelVersion = model_version
            req.ModelFormat = model_format
            
            if gpu_type is not None:
                req.GPUType = gpu_type
            if optimization_level is not None:
                req.OptimizationLevel = optimization_level
            
            if acc_engine_version is not None:
                req.AccEngineVersion = acc_engine_version
            else:
                req.AccEngineVersion = self._describe_model_acc_engine_version(req.ModelFormat)
            req.TensorInfos = tensor_infos
            req.HyperParameter = hyper_parameter
            req.Tags = tags
            req.ModelSignature = model_signature
            req.FrameworkVersion = framework_version

            return self._tione_client.CreateModelAccelerateTask(req)
        except TencentCloudSDKException as err:
            raise
    

    def _set_cos_input_path_info(self, req, model_input_path):
        if isinstance(model_input_path, str):
            req.ModelInputPath = self.parse_cos_info(model_input_path)
            return
        cos_path_info = models.CosPathInfo()
        cos_path_info.Bucket = model_input_path.Bucket
        cos_path_info.Region = model_input_path.Region
        cos_path_info.Paths = [model_input_path.Path]
        cos_path_info.Uin = model_input_path.Uin
        cos_path_info.SubUin = model_input_path.SubUin
        req.ModelInputPath = cos_path_info
    
    
    def _describe_model_acc_engine_version(self, model_format):
        versions = self.describe_model_acc_engine_versions()
        ret = "v1.0"
        for engine_version in versions.ModelAccEngineVersions:
            if model_format == engine_version.ModelFormat:
                for version in engine_version.EngineVersions:
                    if len(version.Version) > len(ret) or (len(version.Version) == len(ret) and version.Version > ret):
                        ret = version.Version
        return ret

    
    def describe_model_accelerate_task(self, model_acc_task_id):
        """查询模型加速任务详情

        :param model_acc_task_id:        模型加速任务id
        :type model_acc_task_id:         str
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.DescribeModelAccelerateTaskResponse` 
        """
        try:
            req = models.DescribeModelAccelerateTaskRequest()
            req.ModelAccTaskId = model_acc_task_id
            return self._tione_client.DescribeModelAccelerateTask(req)
        except TencentCloudSDKException as err:
            raise


    def stop_model_accelerate_task(self, model_acc_task_id):
        """停止模型加速任务

        :param model_acc_task_id:        模型加速任务id
        :type model_acc_task_id:         str
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.StopModelAccelerateTaskResponse` 
        """
        try:
            req = models.StopModelAccelerateTaskRequest()
            req.ModelAccTaskId = model_acc_task_id
            return self._tione_client.StopModelAccelerateTask(req)
        except TencentCloudSDKException as err:
            raise


    def delete_model_accelerate_task(self, model_acc_task_id):
        """删除模型加速任务

        :param model_acc_task_id:        模型加速任务id
        :type model_acc_task_id:         str
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.DeleteModelAccelerateTaskResponse` 
        """
        try:
            req = models.DeleteModelAccelerateTaskRequest()
            req.ModelAccTaskId = model_acc_task_id
            return self._tione_client.DeleteModelAccelerateTask(req)
        except TencentCloudSDKException as err:
            raise

    
    def restart_model_accelerate_task(self,
                             model_acc_task_id,
                             model_source,
                             model_format,
                             model_name,
                             model_version,
                             model_input_path,
                             model_output_path,
                             tensor_infos,
                             acc_engine_version = None,
                             optimization_level = None,
                             gpu_type = None,
                             hyper_parameter = None,
                             tags = None,
                             model_signature = None):
        """重启模型加速任务
        :param model_acc_task_id:        任务id
        :type model_acc_task_id:         str
        :param model_source:             模型来源(JOB/COS)
        :type model_source:              str
        :param model_format:             模型格式(TORCH_SCRIPT/DETECTRON2/SAVED_MODEL/FROZEN_GRAPH/MMDETECTION/ONNX/HUGGING_FACE)
        :type model_format:              str
        :param model_name:               模型名称
        :type model_name:                str
        :param model_version:            模型版本
        :type model_version:             str
        :param model_input_path:         模型输入cos地址
        :type model_input_path:          :class:`tikit.models.CosPathInfo`
        :param model_output_path:        优化模型输出cos地址
        :type model_output_path:         :class:`tikit.models.CosPathInfo`
        :param tensor_infos:             tensor信息
        :type tensor_infos:              list of str
        :param acc_engine_version:       优化引擎版本，不传则默认使用最新版本
        :type acc_engine_version:        str
        :param optimization_level:       优化级别(NO_LOSS/FP16/INT8),默认FP16
        :type optimization_level:        str
        :param gpu_type:                 GPU卡类型(T4/V100),默认T4
        :type gpu_type:                  str
        :param hyper_parameter:          专业参数设置
        :type hyper_parameter:           :class:`tikit.tencentcloud.tione.v20211111.models.HyperParameter`
        :param tags:                     标签
        :type tags:                      list of :class:`tikit.tencentcloud.tione.v20211111.models.Tag`
        :param model_signature:          SAVED_MODEL保存时配置的签名
        :type model_signature:           str
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.RestartModelAccelerateTaskResponse` 
        """
        try:
            req = models.RestartModelAccelerateTaskRequest()
            req.ModelAccTaskId = model_acc_task_id
            req.ModelSource = model_source
            self._set_cos_input_path_info(req, model_input_path)
            self._set_cos_path_info(req, model_output_path)
            req.ModelName = model_name
            req.ModelVersion = model_version
            req.ModelFormat = model_format
            if gpu_type is not None:
                req.GPUType = gpu_type
            if optimization_level is not None:
                req.OptimizationLevel = optimization_level

            if acc_engine_version is not None:
                req.AccEngineVersion = acc_engine_version
            else:
                req.AccEngineVersion = self._describe_model_acc_engine_version(req.ModelFormat)

            req.TensorInfos = tensor_infos
            req.HyperParameter = hyper_parameter
            req.Tags = tags
            req.ModelSignature = model_signature

            return self._tione_client.RestartModelAccelerateTask(req)
        except TencentCloudSDKException as err:
            raise
     

    def create_optimized_model(self, model_acc_task_id, tags = None):
        """保存优化模型

        :param model_acc_task_id:        模型加速任务id
        :type model_acc_task_id:         str
        :param tags:                     标签
        :type tags:                      list of :class:`tikit.tencentcloud.tione.v20211111.models.Tag`
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.CreateOptimizedModelResponse`
        """
        try:
            req = models.CreateOptimizedModelRequest()
            req.ModelAccTaskId = model_acc_task_id
            req.Tags = tags
            return self._tione_client.CreateOptimizedModel(req)
        except TencentCloudSDKException as err:
            raise
      

    def describe_model_acc_engine_versions(self):
        """查询模型加速引擎版本列表
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.DescribeModelAccEngineVersionsResponse`
        """
        try:
            req = models.DescribeModelAccEngineVersionsRequest()
            return self._tione_client.DescribeModelAccEngineVersions(req)
        except TencentCloudSDKException as err:
            raise

    
    def create_batch_model_acc_tasks(self,
                                    model_acc_task_name,
                                    batch_model_acc_tasks,
                                    model_output_path,
                                    optimization_level = None,
                                    gpu_type = None,
                                    hyper_parameter = None,
                                    tags = None):
        """批量创建模型加速任务

        :param model_acc_task_name:        任务名称
        :type model_acc_task_name:         str
        :param batch_model_acc_tasks:      批量模型加速任务
        :type batch_model_acc_tasks:       list of tikit.tencentcloud.tione.v20211111.models.BatchModelAccTask
        :param model_output_path:          模型加速保存路径
        :type model_output_path:           :class:`tikit.models.CosPathInfo`
        :param optimization_level:         优化级别(NO_LOSS/FP16/INT8)，默认FP16
        :type optimization_level:          str
        :param gpu_type:                   GPU卡类型(T4/V100)，默认T4
        :type gpu_type:                    str
        :param hyper_parameter:            专业参数设置
        :type hyper_parameter:             :class:`tikit.tencentcloud.tione.v20211111.models.HyperParameter`
        :param tags:                       标签
        :type tags:                        list of tikit.tencentcloud.tione.v20211111.models.Tag
        :rtype:                            :class:`tikit.tencentcloud.tione.v20211111.models.CreateBatchModelAccTasksResponse`
        """
        try:
            req = models.CreateBatchModelAccTasksRequest()
            req.ModelAccTaskName = model_acc_task_name
            self._set_cos_path_info(req, model_output_path)
            if gpu_type is not None:
                req.GPUType = gpu_type
            if optimization_level is not None:
                req.OptimizationLevel = optimization_level
            req.HyperParameter = hyper_parameter
            req.Tags = tags
            if batch_model_acc_tasks is not None:
                self._set_batch_model_acc_task(req, batch_model_acc_tasks)
            
            return self._tione_client.CreateBatchModelAccTasks(req)
        except TencentCloudSDKException as err:
            raise

    
    def _set_batch_model_acc_task(self, req, batch_model_acc_tasks):
        batch_tasks = []
        for batch_model_acc_task in batch_model_acc_tasks:
            batch_task = models.BatchModelAccTask()
            batch_task.ModelName = batch_model_acc_task.ModelName
            batch_task.ModelVersion = batch_model_acc_task.ModelVersion
            batch_task.ModelFormat = batch_model_acc_task.ModelFormat
            batch_task.ModelSource = batch_model_acc_task.ModelSource
            batch_task.ModelInputPath = batch_model_acc_task.ModelInputPath
            batch_task.TensorInfos = batch_model_acc_task.TensorInfos
            batch_task.ModelSignature = batch_model_acc_task.ModelSignature

            if batch_model_acc_task.AccEngineVersion is not None:
                batch_task.AccEngineVersion = batch_model_acc_task.AccEngineVersion
            else:
                batch_task.AccEngineVersion = self._describe_model_acc_engine_version(batch_task.ModelFormat)
            
            batch_tasks.append(batch_task)
        
        req.BatchModelAccTasks = batch_tasks

    def describe_model_acc_optimized_report(self, model_acc_task_id):
        """查询模型加速任务效果报告

        :param model_acc_task_id: 模型加速任务id
        :type model_acc_task_id: str
        :rtype: :class:`tikit.tencentcloud.tione.v20211111.models.DescribeModelAccOptimizedReportResponse`
        """
        try:
            req = models.DescribeModelAccOptimizedReportRequest()
            req.ModelAccTaskId = model_acc_task_id
            return self._tione_client.DescribeModelAccOptimizedReport(req)
        except TencentCloudSDKException as err:
            raise
             


