🚀 Попробуйте Zilliz Cloud, полностью управляемый Milvus, бесплатно — ощутите 10-кратное увеличение производительности! Попробовать сейчас>

milvus-logo
LFAI

Обзор

  • Engineering
September 15, 2021
Zhen Chen

Обзор

URL проекта: https://github.com/milvus-io/milvus_cli

Подготовка: Python3.8, Click 8.0.x

Группировка команд

Создание команды

import click
from utils import PyOrm

@click.group(no_args_is_help=False, add_help_option=False, invoke_without_command=True)
@click.pass_context
def cli(ctx):
    """Milvus CLI"""
    ctx.obj = PyOrm() # PyOrm is a util class which wraps the milvus python SDK. You can pass any class instance here. Any command function passed by @click.obj can call it.

if __name__ == '__main__':
    cli()

Как показано в коде выше, мы используем @click.group() для создания группы команд cli в качестве точки входа. Чтобы реализовать подсказку CLI, нам нужно отключить справочные сообщения для входа, поэтому мы добавляем no_args_is_help=False, add_help_option=False и invoke_without_command=True. И ничего не будет выведено, если мы введем cli только в терминале.

Кроме того, мы используем @click.pass_context для передачи контекста в эту группу для дальнейшего использования.

Создание подкоманды группы команд

Затем мы добавляем первую подкоманду help в группу cli:

# Print the help message of specified command.
def print_help_msg(command):
    with click.Context(command) as ctx:
        click.echo(command.get_help(ctx))


# Use @cli.command() to create a sub command of cli.
@cli.command()
def help():
    """Show help messages."""
    # Print help message of cli.
    click.echo(print_help_msg(cli))

Теперь мы можем использовать cli help в терминале:

$ python milvus_cli/scripts/milvus_cli.py help

Создание подгруппы группы команд

Мы хотим иметь не только подкоманду cli help, но и подгруппу команд cli list collection, cli list partition и cli list indexes.

Сначала мы создадим подгруппу команд list, здесь мы можем передать первый параметр @cli.group в качестве имени команды вместо использования имени функции defult, так что мы можем уменьшить дублирование имен функций.

Внимание, здесь мы используем @cli.group() вместо @click.group, чтобы создать подгруппу группы происхождения.

Затем мы используем @click.pass_obj для передачи context.obj подкомандам этой подгруппы.

@cli.group('list', no_args_is_help=False)
@click.pass_obj
def listDetails(obj):
    """List collections, partitions and indexes."""
    pass

Затем мы добавляем некоторые подкоманды в эту подгруппу с помощью @listDetails.command() (не @cli.command()). Это просто пример, вы можете проигнорировать его, и мы обсудим его позже.

@listDetails.command()
@click.option('--timeout', 'timeout', help="[Optional] - An optional duration of time in seconds to allow for the RPC. When timeout is set to None, client waits until server response or error occur.", default=None)
@click.option('--show-loaded', 'showLoaded', help="[Optional] - Only show loaded collections.", default=False)
@click.pass_obj
def collections(obj, timeout, showLoaded):
    """List all collections."""
    try:
        obj.checkConnection()
        click.echo(obj.listCollections(timeout, showLoaded))
    except Exception as e:
        click.echo(message=e, err=True)


@listDetails.command()
@click.option('-c', '--collection', 'collection', help='The name of collection.', default='')
@click.pass_obj
def partitions(obj, collection):
    """List all partitions of the specified collection."""
    try:
        obj.checkConnection()
        validateParamsByCustomFunc(
            obj.getTargetCollection, 'Collection Name Error!', collection)
        click.echo(obj.listPartitions(collection))
    except Exception as e:
        click.echo(message=e, err=True)

После всего этого у нас есть команда miltigroup, которая выглядит следующим образом:

image image

Пользовательская команда

Добавление опций

Вы можете добавить некоторые опции к команде, которые будут использоваться как cli --test-option value.

Вот пример, мы добавляем три параметра alias, host и port для указания адреса для подключения к Milvus.

Первые два параметра определяют краткое и полное имя опции, третий параметр - имя переменной, параметр help - краткое справочное сообщение, параметр default - значение по умолчанию, а type - тип значения.

Значения всех опций будут переданы в функцию в порядке их определения.

@cli.command(no_args_is_help=False)
@click.option('-a', '--alias', 'alias', help="Milvus link alias name, default is `default`.", default='default', type=str)
@click.option('-h', '--host', 'host', help="Host name, default is `127.0.0.1`.", default='127.0.0.1', type=str)
@click.option('-p', '--port', 'port', help="Port, default is `19530`.", default=19530, type=int)
@click.pass_obj
def connect(obj, alias, host, port):
    pass

Добавление опций флага

Выше мы использовали опции для передачи значения, но иногда нам просто нужен флаг в виде булевого значения.

Как показано в примере ниже, опция autoId является опцией флага и не передает никаких данных в функцию, поэтому мы можем использовать ее как cli create collection -c c_name -p p_name -a.

@createDetails.command('collection')
@click.option('-c', '--collection-name', 'collectionName', help='Collection name to be created.', default='')
@click.option('-p', '--schema-primary-field', 'primaryField', help='Primary field name.', default='')
@click.option('-a', '--schema-auto-id', 'autoId', help='Enable auto id.', default=False, is_flag=True)
@click.pass_obj
def createCollection(obj, collectionName, primaryField, autoId, description, fields):
    pass

Добавляем аргументы

В этом проекте мы заменили использование аргументов на использование опций. Но здесь мы все же представим использование аргументов. В отличие от опций, аргументы используются как cli COMMAND [OPTIONS] ARGUEMENTS. Если мы преобразуем пример выше в использование аргументов, это будет выглядеть так:

@createDetails.command('collection')
@click.argument('collectionName')
@click.option('-p', '--schema-primary-field', 'primaryField', help='Primary field name.', default='')
@click.option('-a', '--schema-auto-id', 'autoId', help='Enable auto id.', default=False, is_flag=True)
@click.pass_obj
def createCollection(obj, collectionName, primaryField, autoId, description, fields):
    pass

Тогда использование должно выглядеть так: cli create collection c_name -p p_name -a.

Добавьте полное сообщение справки

Как мы определили краткое сообщение справки выше, мы можем определить полное сообщение справки в функции:

@cli.command(no_args_is_help=False)
@click.option('-a', '--alias', 'alias', help="Milvus link alias name, default is `default`.", default='default', type=str)
@click.option('-h', '--host', 'host', help="Host name, default is `127.0.0.1`.", default='127.0.0.1', type=str)
@click.option('-p', '--port', 'port', help="Port, default is `19530`.", default=19530, type=int)
@click.pass_obj
def connect(obj, alias, host, port):
    """
    Connect to Milvus.

    Example:

        milvus_cli > connect -h 127.0.0.1 -p 19530 -a default
    """
    try:
        obj.connect(alias, host, port)
    except Exception as e:
        click.echo(message=e, err=True)
    else:
        click.echo("Connect Milvus successfully!")
        click.echo(obj.showConnection(alias))

Первый блок внутри функции - это справочное сообщение, которое будет выведено после ввода cli connect --help.

milvus_cli > connect --help
Usage: milvus_cli.py connect [OPTIONS]

  Connect to Milvus.

  Example:

      milvus_cli > connect -h 127.0.0.1 -p 19530 -a default

Options:
  -a, --alias TEXT    Milvus link alias name, default is `default`.
  -h, --host TEXT     Host name, default is `127.0.0.1`.
  -p, --port INTEGER  Port, default is `19530`.
  --help              Show this message and exit.

Добавить подтверждение

Иногда нам нужно, чтобы пользователь подтвердил какое-то действие, особенно удаление чего-либо. Мы можем добавить click.confirm, чтобы сделать паузу и попросить пользователя подтвердить действие:

@deleteSth.command('collection')
@click.option('-c', '--collection', 'collectionName', help='The name of collection to be deleted.', default='')
@click.option('-t', '--timeout', 'timeout', help='An optional duration of time in seconds to allow for the RPC. If timeout is set to None, the client keeps waiting until the server responds or an error occurs.', default=None, type=int)
@click.pass_obj
def deleteCollection(obj, collectionName, timeout):
    """
    Drops the collection together with its index files.

    Example:

        milvus_cli > delete collection -c car
    """
    click.echo(
        "Warning!\nYou are trying to delete the collection with data. This action cannot be undone!\n")
    if not click.confirm('Do you want to continue?'):
        return
    pass

Как показано в примере выше, подтверждение разговора будет выглядеть так: Aborted!ant to continue? [y/N]:.

Добавить подсказки

Для реализации подсказок нам нужно всего лишь добавить click.prompt.

@cli.command()
@click.pass_obj
def query(obj):
    """
    Query with a set of criteria, and results in a list of records that match the query exactly.
    """
    collectionName = click.prompt(
        'Collection name', type=click.Choice(obj._list_collection_names()))
    expr = click.prompt('The query expression(field_name in [x,y])')
    partitionNames = click.prompt(
        f'The names of partitions to search(split by "," if multiple) {obj._list_partition_names(collectionName)}', default='')
    outputFields = click.prompt(
        f'Fields to return(split by "," if multiple) {obj._list_field_names(collectionName)}', default='')
    timeout = click.prompt('timeout', default='')
    pass

Подсказка будет появляться при каждом click.prompt. Мы используем несколько подсказок последовательно, чтобы это выглядело как непрерывный разговор. Это гарантирует, что пользователь будет вводить данные в нужном нам порядке. В данном случае нам нужно, чтобы пользователь сначала выбрал коллекцию, а затем получил все разделы из этой коллекции и показал их пользователю для выбора.

Добавить варианты

Иногда вы хотите, чтобы пользователь просто ввел ограниченный диапазон/тип значений, вы можете добавить type=click.Choice([<any>]) к click.prompt, click.options и т. д..

Например,

collectionName = click.prompt(
        'Collection name', type=click.Choice(['collection_1', 'collection_2']))

Тогда пользователь может вводить только collection_1 или collection_2, при любом другом вводе будет выдана ошибка.

Добавить прозрачный экран

Для этого можно использовать click.clear().

@cli.command()
def clear():
    """Clear screen."""
    click.clear()

Дополнительные советы

  • Значение по умолчанию - None, поэтому бессмысленно указывать значение по умолчанию None. А значение по умолчанию None приведет к постоянному отображению click.prompt, если вы хотите оставить значение пустым, чтобы перепрыгнуть через него.

Реализация подсказки CLI для ввода пользователем

Зачем нужен prompt CLI

Для работы с базой данных нам необходимо постоянное соединение с экземпляром. Если мы используем режим командной строки origin, соединение будет обрываться после каждой выполненной команды. Мы также хотим хранить некоторые данные при использовании CLI и очищать их после выхода.

Реализуйте

  1. Используйте while True для постоянного прослушивания пользовательского ввода.
def runCliPrompt():
    while True:
        astr = input('milvus_cli > ')
        try:
            cli(astr.split())
        except SystemExit:
            # trap argparse error message
            # print('error', SystemExit)
            continue


if __name__ == '__main__':
    runCliPrompt()
  1. Использование input приведет к тому, что клавиши со стрелками up, down, left, right, tab и некоторые другие клавиши будут автоматически преобразовываться в строку Acsii. Кроме того, команды истории не могут быть прочитаны из сессии. Поэтому мы добавим readline в функцию runCliPrompt.
def runCliPrompt():
    while True:
            import readline
        readline.set_completer_delims(' \t\n;')
        astr = input('milvus_cli > ')
        try:
            cli(astr.split())
        except SystemExit:
            # trap argparse error message
            # print('error', SystemExit)
            continue
  1. Добавьте quit CLI.
@cli.command('exit')
def quitapp():
    """Exit the CLI."""
    global quitapp
    quitapp = True


quitapp = False  # global flag


def runCliPrompt():
    while not quitapp:
            import readline
        readline.set_completer_delims(' \t\n;')
        astr = input('milvus_cli > ')
        try:
            cli(astr.split())
        except SystemExit:
            # trap argparse error message
            # print('error', SystemExit)
            continue
  1. Перехват ошибки KeyboardInterrupt при использовании ctrl C для выхода.
def runCliPrompt():
    try:
        while not quitapp:
            import readline
            readline.set_completer_delims(' \t\n;')
            astr = input('milvus_cli > ')
            try:
                cli(astr.split())
            except SystemExit:
                # trap argparse error message
                # print('error', SystemExit)
                continue
    except KeyboardInterrupt:
        sys.exit(0)
  1. После всех этих действий CLI теперь выглядит так:
milvus_cli >
milvus_cli > connect
+-------+-----------+
| Host  | 127.0.0.1 |
| Port  |   19530   |
| Alias |  default  |
+-------+-----------+

milvus_cli > help
Usage:  [OPTIONS] COMMAND [ARGS]...

  Milvus CLI

Commands:
  clear     Clear screen.
  connect   Connect to Milvus.
  create    Create collection, partition and index.
  delete    Delete specified collection, partition and index.
  describe  Describe collection or partition.
  exit      Exit the CLI.
  help      Show help messages.
  import    Import data from csv file with headers and insert into target...
  list      List collections, partitions and indexes.
  load      Load specified collection.
  query     Query with a set of criteria, and results in a list of...
  release   Release specified collection.
  search    Conducts a vector similarity search with an optional boolean...
  show      Show connection, loading_progress and index_progress.
  version   Get Milvus CLI version.

milvus_cli > exit

Ручная реализация автозаполнения

В отличие от автозаполнения оболочки в click, наш проект оборачивает командную строку и использует цикл для получения пользовательского ввода для реализации быстрого ввода командной строки. Поэтому нам нужно привязать компилятор к readline.

class Completer(object):
    RE_SPACE = re.compile('.*\s+$', re.M)
    CMDS_DICT = {
        'clear': [],
        'connect': [],
        'create': ['collection', 'partition', 'index'],
        'delete': ['collection', 'partition', 'index'],
        'describe': ['collection', 'partition'],
        'exit': [],
        'help': [],
        'import': [],
        'list': ['collections', 'partitions', 'indexes'],
        'load': [],
        'query': [],
        'release': [],
        'search': [],
        'show': ['connection', 'index_progress', 'loading_progress'],
        'version': [],
    }

    def __init__(self) -> None:
        super().__init__()
        self.COMMANDS = list(self.CMDS_DICT.keys())
        self.createCompleteFuncs(self.CMDS_DICT)

    def createCompleteFuncs(self, cmdDict):
        for cmd in cmdDict:
            sub_cmds = cmdDict[cmd]
            complete_example = self.makeComplete(cmd, sub_cmds)
            setattr(self, 'complete_%s' % cmd, complete_example)

    def makeComplete(self, cmd, sub_cmds):
        def f_complete(args):
            f"Completions for the {cmd} command."
            if not args:
                return self._complete_path('.')
            if len(args) <= 1 and not cmd == 'import':
                return self._complete_2nd_level(sub_cmds, args[-1])
            return self._complete_path(args[-1])
        return f_complete

    def _listdir(self, root):
        "List directory 'root' appending the path separator to subdirs."
        res = []
        for name in os.listdir(root):
            path = os.path.join(root, name)
            if os.path.isdir(path):
                name += os.sep
            res.append(name)
        return res

    def _complete_path(self, path=None):
        "Perform completion of filesystem path."
        if not path:
            return self._listdir('.')
        dirname, rest = os.path.split(path)
        tmp = dirname if dirname else '.'
        res = [os.path.join(dirname, p)
               for p in self._listdir(tmp) if p.startswith(rest)]
        # more than one match, or single match which does not exist (typo)
        if len(res) > 1 or not os.path.exists(path):
            return res
        # resolved to a single directory, so return list of files below it
        if os.path.isdir(path):
            return [os.path.join(path, p) for p in self._listdir(path)]
        # exact file match terminates this completion
        return [path + ' ']

    def _complete_2nd_level(self, SUB_COMMANDS=[], cmd=None):
        if not cmd:
            return [c + ' ' for c in SUB_COMMANDS]
        res = [c for c in SUB_COMMANDS if c.startswith(cmd)]
        if len(res) > 1 or not (cmd in SUB_COMMANDS):
            return res
        return [cmd + ' ']

    def complete(self, text, state):
        "Generic readline completion entry point."
        buffer = readline.get_line_buffer()
        line = readline.get_line_buffer().split()
        # show all commands
        if not line:
            return [c + ' ' for c in self.COMMANDS][state]
        # account for last argument ending in a space
        if self.RE_SPACE.match(buffer):
            line.append('')
        # resolve command to the implementation function
        cmd = line[0].strip()
        if cmd in self.COMMANDS:
            impl = getattr(self, 'complete_%s' % cmd)
            args = line[1:]
            if args:
                return (impl(args) + [None])[state]
            return [cmd + ' '][state]
        results = [
            c + ' ' for c in self.COMMANDS if c.startswith(cmd)] + [None]
        return results[state]

После определения Completer мы можем связать его с readline:

comp = Completer()


def runCliPrompt():
    try:
        while not quitapp:
            import readline
            readline.set_completer_delims(' \t\n;')
            readline.parse_and_bind("tab: complete")
            readline.set_completer(comp.complete)
            astr = input('milvus_cli > ')
            try:
                cli(astr.split())
            except SystemExit:
                # trap argparse error message
                # print('error', SystemExit)
                continue
    except KeyboardInterrupt:
        sys.exit(0)

Добавить одноразовую опцию

Для командной строки быстрого доступа иногда мы не хотим полностью запускать скрипты, чтобы получить некоторую информацию, например версию. Хорошим примером является Python, когда вы вводите python в терминале, появляется командная строка promtp, но она возвращает только сообщение о версии и не вводит скрипты prompt, если вы вводите python -V. Поэтому мы можем использовать sys.args в нашем коде для реализации.

def runCliPrompt():
    args = sys.argv
    if args and (args[-1] == '--version'):
        print(f"Milvus Cli v{getPackageVersion()}")
        return
    try:
        while not quitapp:
            import readline
            readline.set_completer_delims(' \t\n;')
            readline.parse_and_bind("tab: complete")
            readline.set_completer(comp.complete)
            astr = input('milvus_cli > ')
            try:
                cli(astr.split())
            except SystemExit:
                # trap argparse error message
                # print('error', SystemExit)
                continue
    except KeyboardInterrupt:
        sys.exit(0)


if __name__ == '__main__':
    runCliPrompt()

При первом запуске CLI-скриптов мы получим sys.args перед циклом. Если последним аргументом будет --version, то код вернет версию пакета, не запуская цикл.

Это будет полезно после того, как мы соберем коды в виде пакета. Пользователь может набрать milvus_cli, чтобы перейти к приглашению CLI, или набрать milvus_cli --version, чтобы получить только версию.

Сборка и выпуск

Наконец, мы хотим собрать пакет и выпустить его с помощью PYPI. Чтобы пользователь мог просто использовать pip install <package name> для установки.

Локальная установка для тестирования

Прежде чем публиковать пакет в PYPI, вы можете захотеть установить его локально для некоторых тестов.

В этом случае вы можете просто cd в каталог пакета и запустить pip install -e . (не забудьте про .).

Создание файлов пакета

См.: https://packaging.python.org/tutorials/packaging-projects/

Структура пакета должна выглядеть следующим образом:

package_example/
├── LICENSE
├── README.md
├── setup.py
├── src/
│   ├── __init__.py
│   ├── main.py
│   └── scripts/
│       ├── __init__.py
│       └── example.py
└── tests/

Создайте каталог пакета

Создайте каталог Milvus_cli со структурой, приведенной ниже:

Milvus_cli/
├── LICENSE
├── README.md
├── setup.py
├── milvus_cli/
│   ├── __init__.py
│   ├── main.py
│   ├── utils.py
│   └── scripts/
│       ├── __init__.py
│       └── milvus_cli.py
└── dist/

Напишите код входа

Запись скрипта должна находиться в каталоге Milvus_cli/milvus_cli/scripts, а каталог Milvus_cli/milvus_cli/scripts/milvus_cli.py должен иметь следующий вид:

import sys
import os
import click
from utils import PyOrm, Completer


pass_context = click.make_pass_decorator(PyOrm, ensure=True)


@click.group(no_args_is_help=False, add_help_option=False, invoke_without_command=True)
@click.pass_context
def cli(ctx):
    """Milvus CLI"""
    ctx.obj = PyOrm()

"""
...
Here your code.
...
"""

@cli.command('exit')
def quitapp():
    """Exit the CLI."""
    global quitapp
    quitapp = True


quitapp = False  # global flag
comp = Completer()


def runCliPrompt():
    args = sys.argv
    if args and (args[-1] == '--version'):
        print(f"Milvus Cli v{getPackageVersion()}")
        return
    try:
        while not quitapp:
            import readline
            readline.set_completer_delims(' \t\n;')
            readline.parse_and_bind("tab: complete")
            readline.set_completer(comp.complete)
            astr = input('milvus_cli > ')
            try:
                cli(astr.split())
            except SystemExit:
                # trap argparse error message
                # print('error', SystemExit)
                continue
            except Exception as e:
                click.echo(
                    message=f"Error occurred!\n{str(e)}", err=True)
    except KeyboardInterrupt:
        sys.exit(0)


if __name__ == '__main__':
    runCliPrompt()

Отредактируйте setup.py

from setuptools import setup, find_packages

with open("README.md", "r", encoding="utf-8") as fh:
    long_description = fh.read()

setup(
    name='milvus_cli',
    version='0.1.6',
    author='Milvus Team',
    author_email='milvus-team@zilliz.com',
    url='https://github.com/milvus-io/milvus_cli',
    description='CLI for Milvus',
    long_description=long_description,
    long_description_content_type='text/markdown',
    license='Apache-2.0',
    packages=find_packages(),
    include_package_data=True,
    install_requires=[
        'Click==8.0.1',
        'pymilvus==2.0.0rc5',
        'tabulate==0.8.9'
    ],
    entry_points={
        'console_scripts': [
            'milvus_cli = milvus_cli.scripts.milvus_cli:runCliPrompt',
        ],
    },
    python_requires='>=3.8'
)

Несколько советов:

  1. Мы используем содержимое README.md в качестве длинного описания пакета.
  2. Добавьте все зависимости в install_requires.
  3. Укажите entry_points. В данном случае мы устанавливаем milvus_cli в качестве дочернего элемента console_scripts, чтобы мы могли ввести milvus_cli в качестве команды непосредственно после установки этого пакета. А точкой входа milvus_cli является функция runCliPrompt в milvus_cli/scripts/milvus_cli.py.

Сборка

  1. Обновите пакет build: python3 -m pip install --upgrade build

  2. Запустите build: python -m build --sdist --wheel --outdir dist/ .

  3. В каталоге dist/ будут сгенерированы два файла:

dist/
  example_package_YOUR_USERNAME_HERE-0.0.1-py3-none-any.whl
  example_package_YOUR_USERNAME_HERE-0.0.1.tar.gz

Опубликовать релиз

Обратитесь к: https://packaging.python.org/tutorials/packaging-projects/#uploading-the-distribution-archives

  1. Обновить пакет twine: python3 -m pip install --upgrade twine
  2. Загрузите на PYPI тестовую среду: python3 -m twine upload --repository testpypi dist/*
  3. Загрузить на PYPI: python3 -m twine upload dist/*

CI/CD с помощью рабочих процессов Github

Ссылайтесь на: https://packaging.python.org/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/

Нам нужен способ автоматической загрузки активов, он может собирать пакеты и загружать их в релизы на github и PYPI.

(По некоторым причинам мы хотим, чтобы рабочий процесс публиковал только релиз для тестирования PYPI).

# This is a basic workflow to help you get started with Actions

name: Update the release's assets after it published

# Controls when the workflow will run
on:
  release:
    # The workflow will run after release published
    types: [published]

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called "build"
  build:
    # The type of runner that the job will run on
    runs-on: ubuntu-latest

    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
      # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
      - uses: actions/checkout@v2
      - uses: actions/setup-python@v2
        with:
          python-version: '3.8'
          architecture: 'x64'
      - name: Install pypa/build
        run: >-
          python -m
          pip install
          build
          --user
      - name: Clean dist/
        run: |
          sudo rm -fr dist/*
      - name: Build a binary wheel and a source tarball
        run: >-
          python -m
          build
          --sdist
          --wheel
          --outdir dist/
          .
      # Update target github release's assets
      - name: Update assets
        uses: softprops/action-gh-release@v1
        if: startsWith(github.ref, 'refs/tags/')
        with:
          files: ./dist/*
      - name: Publish distribution 📦 to Test PyPI
        if: contains(github.ref, 'beta') && startsWith(github.ref, 'refs/tags')
        uses: pypa/gh-action-pypi-publish@release/v1
        with:
          user: __token__
          password: ${{ secrets.TEST_PYPI_API_TOKEN }}
          repository_url: https://test.pypi.org/legacy/
          packages_dir: dist/
          verify_metadata: false

Подробнее о Milvus

Milvus - это мощный инструмент, способный обеспечить работу огромного количества приложений для искусственного интеллекта и поиска векторного сходства. Чтобы узнать больше о проекте, ознакомьтесь со следующими ресурсами:

  • Читайте наш блог.
  • Общайтесь с нашим сообществом разработчиков открытого кода в Slack.
  • Используйте самую популярную в мире базу данных векторов на GitHub и вносите в нее свой вклад.
  • Быстро протестируйте и разверните приложения ИИ с помощью нашего нового буткемпа.

Like the article? Spread the word

Продолжить чтение