689 lines
26 KiB
Python
689 lines
26 KiB
Python
#!/usr/bin/env python
|
|
import argparse
|
|
import json
|
|
import sys
|
|
import time
|
|
import os
|
|
from http import HTTPStatus
|
|
|
|
import dashscope
|
|
from dashscope.aigc import Generation
|
|
from dashscope.common.constants import (DeploymentStatus, FilePurpose,
|
|
TaskStatus)
|
|
from dashscope.utils.oss_utils import OssUtils
|
|
|
|
|
|
def print_failed_message(rsp):
|
|
print('Failed, request_id: %s, status_code: %s, code: %s, message: %s' %
|
|
(rsp.request_id, rsp.status_code, rsp.code, rsp.message))
|
|
|
|
|
|
def text_generation(args):
|
|
response = Generation.call(args.model, args.prompt, stream=args.stream)
|
|
if args.stream:
|
|
for rsp in response:
|
|
if rsp.status_code == HTTPStatus.OK:
|
|
print(rsp.output)
|
|
print(rsp.usage)
|
|
else:
|
|
print_failed_message(rsp)
|
|
else:
|
|
if response.status_code == HTTPStatus.OK:
|
|
print(response.output)
|
|
print(response.usage)
|
|
else:
|
|
print_failed_message(response)
|
|
|
|
|
|
class FineTunes:
|
|
@classmethod
|
|
def call(cls, args):
|
|
params = {}
|
|
if args.n_epochs is not None:
|
|
params['n_epochs'] = args.n_epochs
|
|
if args.batch_size is not None:
|
|
params['batch_size'] = args.batch_size
|
|
if args.learning_rate is not None:
|
|
params['learning_rate'] = args.learning_rate
|
|
if args.prompt_loss is not None:
|
|
params['prompt_loss'] = args.prompt_loss
|
|
if args.params:
|
|
params.update(args.params)
|
|
|
|
rsp = dashscope.FineTunes.call(
|
|
model=args.model,
|
|
training_file_ids=args.training_file_ids,
|
|
validation_file_ids=args.validation_file_ids,
|
|
mode=args.mode,
|
|
hyper_parameters=params)
|
|
if rsp.status_code == HTTPStatus.OK:
|
|
print('Create fine-tune job success, job_id: %s' %
|
|
rsp.output['job_id'])
|
|
cls.wait(rsp.output['job_id'])
|
|
else:
|
|
print_failed_message(rsp)
|
|
|
|
@classmethod
|
|
def wait(cls, job_id):
|
|
try:
|
|
while True:
|
|
rsp = dashscope.FineTunes.get(job_id)
|
|
if rsp.status_code == HTTPStatus.OK:
|
|
if rsp.output['status'] == TaskStatus.FAILED:
|
|
print('Fine-tune FAILED!')
|
|
break
|
|
elif rsp.output['status'] == TaskStatus.CANCELED:
|
|
print('Fine-tune task CANCELED')
|
|
break
|
|
elif rsp.output['status'] == TaskStatus.RUNNING:
|
|
print(
|
|
'Fine-tuning is RUNNING, start get output stream.')
|
|
cls.stream_events(job_id)
|
|
elif rsp.output['status'] == TaskStatus.SUCCEEDED:
|
|
print('Fine-tune task success, fine-tuned model:%s' %
|
|
rsp.output['finetuned_output'])
|
|
break
|
|
else:
|
|
print('The fine-tune task is: %s' %
|
|
rsp.output['status'])
|
|
time.sleep(30)
|
|
else:
|
|
print_failed_message(rsp)
|
|
except Exception:
|
|
print(
|
|
'You can stream output via: dashscope fine_tunes.stream -j %s'
|
|
% job_id)
|
|
|
|
@classmethod
|
|
def get(cls, args):
|
|
rsp = dashscope.FineTunes.get(args.job)
|
|
if rsp.status_code == HTTPStatus.OK:
|
|
if rsp.output['status'] == TaskStatus.FAILED:
|
|
print('Fine-tune failed!')
|
|
elif rsp.output['status'] == TaskStatus.CANCELED:
|
|
print('Fine-tune task canceled')
|
|
elif rsp.output['status'] == TaskStatus.SUCCEEDED:
|
|
print('Fine-tune task success, fine-tuned model : %s' %
|
|
rsp.output['finetuned_output'])
|
|
else:
|
|
print('The fine-tune task is: %s' % rsp.output['status'])
|
|
else:
|
|
print_failed_message(rsp)
|
|
|
|
@classmethod
|
|
def list(cls, args):
|
|
rsp = dashscope.FineTunes.list(page=args.start_page,
|
|
page_size=args.page_size)
|
|
if rsp.status_code == HTTPStatus.OK:
|
|
if rsp.output is not None:
|
|
for job in rsp.output['jobs']:
|
|
if job['status'] == TaskStatus.SUCCEEDED:
|
|
print(
|
|
'job: %s, status: %s, base model: %s, fine-tuned model: %s' # noqa E501
|
|
% # noqa
|
|
(job['job_id'], job['status'], job['model'],
|
|
job['finetuned_output']))
|
|
else:
|
|
print('job: %s, status: %s, base model: %s' %
|
|
(job['job_id'], job['status'], job['model']))
|
|
else:
|
|
print('There is no fine-tuned model.')
|
|
else:
|
|
print_failed_message(rsp)
|
|
|
|
@classmethod
|
|
def stream_events(cls, job_id):
|
|
# check job status if job is completed, get log.
|
|
rsp = dashscope.FineTunes.get(job_id)
|
|
if rsp.status_code == HTTPStatus.OK:
|
|
if rsp.output['status'] in [
|
|
TaskStatus.FAILED, TaskStatus.CANCELED,
|
|
TaskStatus.SUCCEEDED
|
|
]:
|
|
print('Fine-tune job: %s is %s' %
|
|
(job_id, rsp.output['status']))
|
|
cls.log(job_id)
|
|
return
|
|
else:
|
|
print_failed_message(rsp)
|
|
return
|
|
# start streaming events.
|
|
try:
|
|
stream_events = dashscope.FineTunes.stream_events(job_id)
|
|
for rsp in stream_events:
|
|
if rsp.status_code == HTTPStatus.OK:
|
|
print(rsp.output)
|
|
else:
|
|
print_failed_message(rsp)
|
|
except Exception:
|
|
print(
|
|
'You can stream output via: dashscope fine-tunes.stream -j %s'
|
|
% job_id)
|
|
|
|
@classmethod
|
|
def events(cls, args):
|
|
cls.stream_events(args.job)
|
|
|
|
@classmethod
|
|
def log(cls, job_id):
|
|
start = 1
|
|
n_line = 1000 # 1000 line per request
|
|
while True:
|
|
rsp = dashscope.FineTunes.logs(job_id, offset=start, line=n_line)
|
|
if rsp.status_code == HTTPStatus.OK:
|
|
for line in rsp.output['logs']:
|
|
print(line)
|
|
if rsp.output['total'] < n_line:
|
|
break
|
|
else:
|
|
start += n_line
|
|
else:
|
|
print_failed_message(rsp)
|
|
|
|
@classmethod
|
|
def cancel(cls, args):
|
|
rsp = dashscope.FineTunes.cancel(args.job)
|
|
if rsp.status_code == HTTPStatus.OK:
|
|
print('Cancel fine-tune job: %s success!')
|
|
else:
|
|
print_failed_message(rsp)
|
|
|
|
@classmethod
|
|
def delete(cls, args):
|
|
rsp = dashscope.FineTunes.delete(args.job)
|
|
if rsp.status_code == HTTPStatus.OK:
|
|
print('fine_tune job: %s delete success' % args.job)
|
|
else:
|
|
print_failed_message(rsp)
|
|
|
|
class Oss:
|
|
@classmethod
|
|
def upload(cls, args):
|
|
print('Start oss.upload: model=%s, file=%s, api_key=%s' % (args.model, args.file, args.api_key))
|
|
if not args.file or not args.model:
|
|
print('Please specify the model and file path')
|
|
return
|
|
|
|
file_path = os.path.expanduser(args.file)
|
|
if not os.path.exists(file_path):
|
|
print('File %s does not exist' % file_path)
|
|
return
|
|
|
|
api_key = os.environ.get('DASHSCOPE_API_KEY', args.api_key)
|
|
if not api_key:
|
|
print('Please set your DashScope API key as environment variable '
|
|
'DASHSCOPE_API_KEY or pass it as argument by -k/--api_key')
|
|
return
|
|
|
|
oss_url = OssUtils.upload(model=args.model,
|
|
file_path=file_path,
|
|
api_key=api_key,
|
|
base_address=args.base_url)
|
|
|
|
if not oss_url:
|
|
print('Failed to upload file: %s' % file_path)
|
|
return
|
|
|
|
print('Uploaded oss url: %s' % oss_url)
|
|
|
|
class Files:
|
|
@classmethod
|
|
def upload(cls, args):
|
|
rsp = dashscope.Files.upload(file_path=args.file,
|
|
purpose=args.purpose,
|
|
description=args.description,
|
|
base_address=args.base_url)
|
|
print(rsp)
|
|
if rsp.status_code == HTTPStatus.OK:
|
|
print('Upload success, file id: %s' %
|
|
rsp.output['uploaded_files'][0]['file_id'])
|
|
else:
|
|
print_failed_message(rsp)
|
|
|
|
@classmethod
|
|
def get(cls, args):
|
|
rsp = dashscope.Files.get(file_id=args.id, base_address=args.base_url)
|
|
if rsp.status_code == HTTPStatus.OK:
|
|
if rsp.output:
|
|
print('file info:\n%s' % json.dumps(rsp.output, ensure_ascii=False, indent=4))
|
|
else:
|
|
print('There is no uploaded file.')
|
|
else:
|
|
print_failed_message(rsp)
|
|
|
|
@classmethod
|
|
def list(cls, args):
|
|
rsp = dashscope.Files.list(page=args.start_page,
|
|
page_size=args.page_size,
|
|
base_address=args.base_url)
|
|
if rsp.status_code == HTTPStatus.OK:
|
|
if rsp.output:
|
|
print('file list info:\n%s' % json.dumps(rsp.output, ensure_ascii=False, indent=4))
|
|
else:
|
|
print('There is no uploaded files.')
|
|
else:
|
|
print_failed_message(rsp)
|
|
|
|
@classmethod
|
|
def delete(cls, args):
|
|
rsp = dashscope.Files.delete(args.id, base_address=args.base_url)
|
|
if rsp.status_code == HTTPStatus.OK:
|
|
print('Delete success')
|
|
else:
|
|
print_failed_message(rsp)
|
|
|
|
|
|
class Deployments:
|
|
@classmethod
|
|
def call(cls, args):
|
|
rsp = dashscope.Deployments.call(model=args.model,
|
|
capacity=args.capacity,
|
|
suffix=args.suffix)
|
|
if rsp.status_code == HTTPStatus.OK:
|
|
deployed_model = rsp.output['deployed_model']
|
|
print('Create model: %s deployment' % deployed_model)
|
|
try:
|
|
while True: # wait for deployment ok.
|
|
status = dashscope.Deployments.get(deployed_model)
|
|
if status.status_code == HTTPStatus.OK:
|
|
if status.output['status'] in [
|
|
DeploymentStatus.PENDING,
|
|
DeploymentStatus.DEPLOYING
|
|
]:
|
|
time.sleep(30)
|
|
print('Deployment %s is %s' %
|
|
(deployed_model, status.output['status']))
|
|
else:
|
|
print('Deployment: %s status: %s' %
|
|
(deployed_model, status.output['status']))
|
|
break
|
|
|
|
else:
|
|
print_failed_message(rsp)
|
|
except Exception:
|
|
print('You can get deployment status via: \
|
|
dashscope deployments.get -d %s' % deployed_model)
|
|
else:
|
|
print_failed_message(rsp)
|
|
|
|
@classmethod
|
|
def get(cls, args):
|
|
rsp = dashscope.Deployments.get(args.deploy)
|
|
if rsp.status_code == HTTPStatus.OK:
|
|
print('Deployed model: %s capacity: %s status: %s' %
|
|
(rsp.output['deployed_model'], rsp.output['capacity'],
|
|
rsp.output['status']))
|
|
else:
|
|
print_failed_message(rsp)
|
|
|
|
@classmethod
|
|
def list(cls, args):
|
|
rsp = dashscope.Deployments.list(page_no=args.start_page,
|
|
page_size=args.page_size)
|
|
if rsp.status_code == HTTPStatus.OK:
|
|
if rsp.output is not None:
|
|
if 'deployments' not in rsp.output or len(
|
|
rsp.output['deployments']) == 0:
|
|
print('There is no deployed model!')
|
|
return
|
|
for deployment in rsp.output['deployments']:
|
|
print('Deployed_model: %s, model: %s, status: %s' %
|
|
(deployment['deployed_model'],
|
|
deployment['model_name'], deployment['status']))
|
|
else:
|
|
print('There is no deployed model.')
|
|
else:
|
|
print_failed_message(rsp)
|
|
|
|
@classmethod
|
|
def update(cls, args):
|
|
rsp = dashscope.Deployments.update(args.deployed_model, args.version)
|
|
if rsp.status_code == HTTPStatus.OK:
|
|
if rsp.output is not None:
|
|
if 'deployments' not in rsp.output:
|
|
print('There is no deployed model!')
|
|
return
|
|
for deployment in rsp.output['deployments']:
|
|
print('Deployed_model: %s, model: %s, status: %s' %
|
|
(deployment['deployed_model'],
|
|
deployment['model_name'], deployment['status']))
|
|
else:
|
|
print('There is no deployed model.')
|
|
else:
|
|
print_failed_message(rsp)
|
|
|
|
@classmethod
|
|
def scale(cls, args):
|
|
rsp = dashscope.Deployments.scale(args.deployed_model, args.capacity)
|
|
if rsp.status_code == HTTPStatus.OK:
|
|
if rsp.output is not None:
|
|
print('Deployed_model: %s, model: %s, status: %s' %
|
|
(rsp.output['deployed_model'], rsp.output['model_name'],
|
|
rsp.output['status']))
|
|
else:
|
|
print('There is no deployed model.')
|
|
else:
|
|
print_failed_message(rsp)
|
|
|
|
@classmethod
|
|
def delete(cls, args):
|
|
rsp = dashscope.Deployments.delete(args.deploy)
|
|
if rsp.status_code == HTTPStatus.OK:
|
|
print('Deployed model: %s delete success' % args.deploy)
|
|
else:
|
|
print_failed_message(rsp)
|
|
|
|
|
|
# from: https://gist.github.com/vadimkantorov/37518ff88808af840884355c845049ea
|
|
class ParseKVAction(argparse.Action):
|
|
def __call__(self, parser, namespace, values, option_string=None):
|
|
setattr(namespace, self.dest, dict())
|
|
for each in values:
|
|
try:
|
|
key, value = each.split('=')
|
|
getattr(namespace, self.dest)[key] = value
|
|
except ValueError as ex:
|
|
message = '\nTraceback: {}'.format(ex)
|
|
message += "\nError on '{}' || It should be 'key=value'".format(
|
|
each)
|
|
raise argparse.ArgumentError(self, str(message))
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
prog='dashscope', description='dashscope command line tools.')
|
|
parser.add_argument('-k', '--api-key', help='Dashscope API key.')
|
|
sub_parsers = parser.add_subparsers(help='Api subcommands')
|
|
text_generation_parser = sub_parsers.add_parser('generation.call')
|
|
text_generation_parser.add_argument('-p',
|
|
'--prompt',
|
|
type=str,
|
|
required=True,
|
|
help='Input prompt')
|
|
text_generation_parser.add_argument('-m',
|
|
'--model',
|
|
type=str,
|
|
required=True,
|
|
help='The model to call.')
|
|
text_generation_parser.add_argument('--history',
|
|
type=str,
|
|
required=False,
|
|
help='The history of the request.')
|
|
text_generation_parser.add_argument('-s',
|
|
'--stream',
|
|
default=False,
|
|
action='store_true',
|
|
help='Use stream mode default false.')
|
|
text_generation_parser.set_defaults(func=text_generation)
|
|
fine_tune_call = sub_parsers.add_parser('fine_tunes.call')
|
|
fine_tune_call.add_argument(
|
|
'-t',
|
|
'--training_file_ids',
|
|
required=True,
|
|
nargs='+',
|
|
help='Training file ids which upload by File command.')
|
|
fine_tune_call.add_argument(
|
|
'-v',
|
|
'--validation_file_ids',
|
|
required=False,
|
|
nargs='+',
|
|
default=[],
|
|
help='Validation file ids which upload by File command.')
|
|
fine_tune_call.add_argument('-m',
|
|
'--model',
|
|
type=str,
|
|
required=True,
|
|
help='The based model to start fine-tune.')
|
|
fine_tune_call.add_argument(
|
|
'--mode',
|
|
type=str,
|
|
required=False,
|
|
choices=['sft', 'efficient_sft'],
|
|
help='Select fine-tune mode sft or efficient_sft')
|
|
fine_tune_call.add_argument('-e',
|
|
'--n_epochs',
|
|
type=int,
|
|
required=False,
|
|
help='How many epochs to fine-tune.')
|
|
fine_tune_call.add_argument('-b',
|
|
'--batch_size',
|
|
type=int,
|
|
required=False,
|
|
help='How big is batch_size.')
|
|
fine_tune_call.add_argument('-l',
|
|
'--learning_rate',
|
|
type=float,
|
|
required=False,
|
|
help='The fine-tune learning rate.')
|
|
fine_tune_call.add_argument('-p',
|
|
'--prompt_loss',
|
|
type=float,
|
|
required=False,
|
|
help='The fine-tune prompt loss.')
|
|
fine_tune_call.add_argument(
|
|
'--hyper_parameters',
|
|
nargs='+',
|
|
dest='params',
|
|
action=ParseKVAction,
|
|
help='Extra hyper parameters accepts by key1=value1 key2=value2',
|
|
metavar='KEY1=VALUE1')
|
|
fine_tune_call.set_defaults(func=FineTunes.call)
|
|
fine_tune_get = sub_parsers.add_parser('fine_tunes.get')
|
|
fine_tune_get.add_argument('-j',
|
|
'--job',
|
|
type=str,
|
|
required=True,
|
|
help='The fine-tune job id.')
|
|
fine_tune_get.set_defaults(func=FineTunes.get)
|
|
fine_tune_delete = sub_parsers.add_parser('fine_tunes.delete')
|
|
fine_tune_delete.add_argument('-j',
|
|
'--job',
|
|
type=str,
|
|
required=True,
|
|
help='The fine-tune job id.')
|
|
fine_tune_delete.set_defaults(func=FineTunes.delete)
|
|
fine_tune_stream = sub_parsers.add_parser('fine_tunes.stream')
|
|
fine_tune_stream.add_argument('-j',
|
|
'--job',
|
|
type=str,
|
|
required=True,
|
|
help='The fine-tune job id.')
|
|
fine_tune_stream.set_defaults(func=FineTunes.events)
|
|
fine_tune_list = sub_parsers.add_parser('fine_tunes.list')
|
|
fine_tune_list.add_argument('-s',
|
|
'--start_page',
|
|
type=int,
|
|
default=1,
|
|
help='Start of page, default 1')
|
|
fine_tune_list.add_argument('-p',
|
|
'--page_size',
|
|
type=int,
|
|
default=10,
|
|
help='The page size, default 10')
|
|
fine_tune_list.set_defaults(func=FineTunes.list)
|
|
fine_tune_cancel = sub_parsers.add_parser('fine_tunes.cancel')
|
|
fine_tune_cancel.add_argument('-j',
|
|
'--job',
|
|
type=str,
|
|
required=True,
|
|
help='The fine-tune job id.')
|
|
fine_tune_cancel.set_defaults(func=FineTunes.cancel)
|
|
|
|
oss_upload = sub_parsers.add_parser('oss.upload')
|
|
oss_upload.add_argument(
|
|
'-f',
|
|
'--file',
|
|
type=str,
|
|
required=True,
|
|
help='The file path to upload',
|
|
)
|
|
oss_upload.add_argument(
|
|
'-m',
|
|
'--model',
|
|
type=str,
|
|
required=True,
|
|
help='The model name',
|
|
)
|
|
oss_upload.add_argument(
|
|
'-k',
|
|
'--api_key',
|
|
type=str,
|
|
required=False,
|
|
help='The dashscope api key',
|
|
)
|
|
oss_upload.add_argument(
|
|
'-u',
|
|
'--base_url',
|
|
type=str,
|
|
help='The base url.',
|
|
required=False,
|
|
)
|
|
oss_upload.set_defaults(func=Oss.upload)
|
|
|
|
file_upload = sub_parsers.add_parser('files.upload')
|
|
file_upload.add_argument(
|
|
'-f',
|
|
'--file',
|
|
type=str,
|
|
required=True,
|
|
help='The file path to upload',
|
|
)
|
|
file_upload.add_argument(
|
|
'-p',
|
|
'--purpose',
|
|
default=FilePurpose.fine_tune,
|
|
const=FilePurpose.fine_tune,
|
|
nargs='?',
|
|
help='Purpose to upload file[fine-tune]',
|
|
required=True,
|
|
)
|
|
file_upload.add_argument(
|
|
'-d',
|
|
'--description',
|
|
type=str,
|
|
help='The file description.',
|
|
required=False,
|
|
)
|
|
file_upload.add_argument(
|
|
'-u',
|
|
'--base_url',
|
|
type=str,
|
|
help='The base url.',
|
|
required=False,
|
|
)
|
|
file_upload.set_defaults(func=Files.upload)
|
|
|
|
file_get = sub_parsers.add_parser('files.get')
|
|
file_get.add_argument('-i',
|
|
'--id',
|
|
type=str,
|
|
required=True,
|
|
help='The file ID')
|
|
file_get.add_argument(
|
|
'-u',
|
|
'--base_url',
|
|
type=str,
|
|
help='The base url.',
|
|
required=False,
|
|
)
|
|
file_get.set_defaults(func=Files.get)
|
|
|
|
file_delete = sub_parsers.add_parser('files.delete')
|
|
file_delete.add_argument('-i',
|
|
'--id',
|
|
type=str,
|
|
required=True,
|
|
help='The files ID')
|
|
file_delete.add_argument(
|
|
'-u',
|
|
'--base_url',
|
|
type=str,
|
|
help='The base url.',
|
|
required=False,
|
|
)
|
|
file_delete.set_defaults(func=Files.delete)
|
|
|
|
file_list = sub_parsers.add_parser('files.list')
|
|
file_list.add_argument('-s',
|
|
'--start_page',
|
|
type=int,
|
|
default=1,
|
|
help='Start of page, default 1')
|
|
file_list.add_argument('-p',
|
|
'--page_size',
|
|
type=int,
|
|
default=10,
|
|
help='The page size, default 10')
|
|
file_list.add_argument(
|
|
'-u',
|
|
'--base_url',
|
|
type=str,
|
|
help='The base url.',
|
|
required=False,
|
|
)
|
|
file_list.set_defaults(func=Files.list)
|
|
|
|
deployments_call = sub_parsers.add_parser('deployments.call')
|
|
deployments_call.add_argument('-m',
|
|
'--model',
|
|
required=True,
|
|
help='The model ID')
|
|
deployments_call.add_argument('-s',
|
|
'--suffix',
|
|
required=False,
|
|
help=('The suffix of the deployment, \
|
|
lower cased characters 8 chars max.'))
|
|
deployments_call.add_argument('-c',
|
|
'--capacity',
|
|
type=int,
|
|
required=False,
|
|
default=1,
|
|
help='The target capacity')
|
|
deployments_call.set_defaults(func=Deployments.call)
|
|
|
|
deployments_get = sub_parsers.add_parser('deployments.get')
|
|
deployments_get.add_argument('-d',
|
|
'--deploy',
|
|
required=True,
|
|
help='The deployed model.')
|
|
deployments_get.set_defaults(func=Deployments.get)
|
|
deployments_delete = sub_parsers.add_parser('deployments.delete')
|
|
deployments_delete.add_argument('-d',
|
|
'--deploy',
|
|
required=True,
|
|
help='The deployed model.')
|
|
deployments_delete.set_defaults(func=Deployments.delete)
|
|
deployments_list = sub_parsers.add_parser('deployments.list')
|
|
deployments_list.add_argument('-s',
|
|
'--start_page',
|
|
type=int,
|
|
default=1,
|
|
help='Start of page, default 1')
|
|
deployments_list.add_argument('-p',
|
|
'--page_size',
|
|
type=int,
|
|
default=10,
|
|
help='The page size, default 10')
|
|
deployments_list.set_defaults(func=Deployments.list)
|
|
deployments_scale = sub_parsers.add_parser('deployments.scale')
|
|
deployments_scale.add_argument('-d',
|
|
'--deployed_model',
|
|
type=str,
|
|
required=True,
|
|
help='The deployed model to scale')
|
|
deployments_scale.add_argument('-c',
|
|
'--capacity',
|
|
type=int,
|
|
required=True,
|
|
help='The target capacity')
|
|
deployments_scale.set_defaults(func=Deployments.scale)
|
|
|
|
args = parser.parse_args()
|
|
if args.api_key is not None:
|
|
dashscope.api_key = args.api_key
|
|
args.func(args)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|