🚀 Experimente o Zilliz Cloud, o Milvus totalmente gerenciado, gratuitamente—experimente um desempenho 10x mais rápido! Experimente Agora>>

milvus-logo
LFAI

Visão geral do projeto

  • Engineering
September 15, 2021
Zhen Chen

Visão geral do projeto

URL do projeto: https://github.com/milvus-io/milvus_cli

Preparação: Python3.8, Click 8.0.x

Agrupar comandos

Criar um comando

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()

Conforme o código acima, usamos @click.group() para criar um grupo de comandos cli como ponto de entrada. Para implementar um prompt CLI, precisamos de desativar as mensagens de ajuda para a entrada, por isso adicionamos no_args_is_help=False, add_help_option=False e invoke_without_command=True. E nada será impresso se introduzirmos cli apenas no terminal.

Além disso, utilizamos @click.pass_context para passar um contexto a este grupo para utilização posterior.

Criar um subcomando do grupo de comandos

De seguida, adicionamos o primeiro subcomando help em 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))

Agora podemos utilizar cli help no terminal:

$ python milvus_cli/scripts/milvus_cli.py help

Criar um subgrupo de um grupo de comandos

Não só queremos ter um subcomando como cli help, mas também precisamos de um subgrupo de comandos como cli list collection, cli list partition e cli list indexes.

Em primeiro lugar, criamos um subgrupo de comandos list. Neste caso, podemos passar o primeiro parâmetro para @cli.group como nome do comando em vez de utilizar o nome da função predefinida, de modo a reduzir a duplicação de nomes de funções.

Atenção, aqui utilizamos @cli.group() em vez de @click.group para criar um subgrupo do grupo de origem.

Utilizamos @click.pass_obj para passar context.obj para os subcomandos deste subgrupo.

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

Depois, adicionamos alguns subcomandos a este subgrupo através de @listDetails.command() (e não @cli.command()). Este é apenas um exemplo, pode ignorar a implementação e discuti-la-emos mais tarde.

@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)

Depois de tudo isto concluído, temos um miltigroup de comandos com o seguinte aspeto

image imagem

Personalizar um comando

Adicionar opções

É possível adicionar algumas opções a um comando que será utilizado como cli --test-option value.

Eis um exemplo: adicionamos três opções alias, host e port para especificar um endereço para ligação ao Milvus.

Os dois primeiros parâmetros definem o nome curto e completo da opção, o terceiro parâmetro define o nome da variável, o parâmetro help especifica a mensagem curta de ajuda, o parâmetro default especifica o valor predefinido e o parâmetro type especifica o tipo de valor.

E todos os valores das opções serão passados para a função por ordem de definição.

@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

Adicionar opções de sinalização

Utilizámos as opções acima para passar um valor, mas, por vezes, só precisamos de um sinalizador como um valor booleano.

Como no exemplo abaixo, a opção autoId é uma opção de sinalização e não passa quaisquer dados para a função, pelo que podemos utilizá-la como 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

Adicionar argumentos

Neste projeto, substituímos a utilização de todos os argumentos pela utilização de opções. Mas ainda introduzimos o uso de argumentos aqui. Diferente das opções, os argumentos são usados como cli COMMAND [OPTIONS] ARGUEMENTS. Se convertermos o exemplo acima na utilização de argumentos, será assim:

@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

Então a utilização deve ser cli create collection c_name -p p_name -a.

Adicionar mensagem de ajuda completa

Tal como definimos a mensagem de ajuda curta acima, podemos definir a mensagem de ajuda completa na função:

@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))

O primeiro bloco dentro da função é a mensagem de ajuda que será impressa depois de introduzirmos 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.

Adicionar confirmação

Por vezes, precisamos que o utilizador confirme uma ação, especialmente a eliminação de algo. Podemos adicionar click.confirm para fazer uma pausa e pedir ao utilizador para confirmar:

@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

Como no exemplo acima, uma conversa de confirmação será apresentada como Aborted!ant to continue? [y/N]:.

Adicionar prompts

Para implementar avisos, só precisamos de adicionar 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

O aviso será apresentado quando cada click.prompt. Utilizamos alguns prompts em série para que pareça uma conversa contínua. Isto garante que o utilizador introduzirá os dados pela ordem pretendida. Neste caso, é necessário que o utilizador escolha primeiro uma coleção e que obtenha todas as partições desta coleção e, em seguida, mostre-as ao utilizador para que este as escolha.

Adicionar escolhas

Por vezes, se o utilizador quiser introduzir apenas um intervalo/tipo de valor limitado, pode adicionar type=click.Choice([<any>]) a click.prompt, click.options e etc...

Por exemplo,

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

Assim, o utilizador só pode introduzir collection_1 ou collection_2. Se introduzir outros valores, surgirá um erro.

Adicionar ecrã limpo

Pode utilizar click.clear() para o implementar.

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

Sugestões adicionais

  • O valor predefinido é None, pelo que não faz sentido se especificar o valor predefinido como None. E o valor predefinido None fará com que click.prompt apareça continuamente se quiser deixar um valor vazio para o ultrapassar.

Implementar um prompt CLI para o utilizador introduzir dados

Porquê um prompt CLI

Para o funcionamento da base de dados, precisamos de uma ligação contínua a uma instância. Se utilizarmos o modo de linha de comandos de origem, a ligação será interrompida após cada comando executado. Também queremos armazenar alguns dados quando usamos o CLI, e limpá-los depois de sair.

Implementar

  1. Use while True para ouvir continuamente a entrada do utilizador.
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. Usar apenas input fará com que as teclas de seta up, down, left, right, a tecla tab e algumas outras teclas sejam convertidas em string Acsii automaticamente. Além disso, os comandos do histórico não podem ser lidos da sessão. Portanto, adicionamos readline à função 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. Adicionar 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. Pegar o erro KeyboardInterrupt quando usar ctrl C para sair.
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. Depois de tudo resolvido, a CLI agora se parece com:
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

Implementar manualmente o autocomplete

Diferente do autocomplete do shell do click, nosso projeto envolve a linha de comando e usa um loop para obter a entrada do usuário para implementar uma linha de comando rápida. Portanto, precisamos vincular um complemento a 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]

Depois de definir Completer, podemos associá-lo ao 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)

Adicionar opção única

Para a linha de comando de prompt, às vezes não queremos executar completamente os scripts para obter algumas informações, como a versão. Um bom exemplo é Python, quando digitamos python no terminal, a linha de comando promtp será exibida, mas só retorna uma mensagem de versão e não entrará nos scripts do prompt se digitarmos python -V. Assim, podemos utilizar sys.args no nosso código para implementar.

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()

Obtemos sys.args antes do loop quando corremos pela primeira vez para os scripts CLI. Se os últimos argumentos forem --version, o código devolverá a versão do pacote sem entrar no ciclo.

Será útil depois de construirmos os códigos como um pacote. O utilizador pode escrever milvus_cli para saltar para um prompt CLI, ou escrever milvus_cli --version para obter apenas a versão.

Construir e lançar

Finalmente queremos construir um pacote e liberar pelo PYPI. Assim, o usuário pode simplesmente usar pip install <package name> para instalar.

Instalar localmente para teste

Antes de publicar o pacote no PYPI, pode-se querer instalá-lo localmente para alguns testes.

Neste caso, pode simplesmente cd no diretório do pacote e correr pip install -e . (Não esquecer .).

Criar ficheiros de pacote

Consulte: https://packaging.python.org/tutorials/packaging-projects/

A estrutura de um pacote deve ser semelhante:

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

Criar o diretório do pacote

Crie o diretório Milvus_cli com a estrutura abaixo:

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

Escrever o código de entrada

A entrada do script deve estar em Milvus_cli/milvus_cli/scripts, e o Milvus_cli/milvus_cli/scripts/milvus_cli.py deve ser como:

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()

Editar o diretório 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'
)

Algumas dicas aqui:

  1. Usamos o conteúdo de README.md como a descrição longa do pacote.
  2. Adicione todas as dependências a install_requires.
  3. Especifique o entry_points. Neste caso, definimos milvus_cli como um filho de console_scripts, para que possamos digitar milvus_cli como um comando diretamente após instalarmos este pacote. E o ponto de entrada de milvus_cli é a função runCliPrompt em milvus_cli/scripts/milvus_cli.py.

Construir

  1. Actualize o pacote build: python3 -m pip install --upgrade build

  2. Execute build: python -m build --sdist --wheel --outdir dist/ .

  3. Serão gerados dois ficheiros no diretório dist/:

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

Publicar versão

Consulte: https://packaging.python.org/tutorials/packaging-projects/#uploading-the-distribution-archives

  1. Atualizar o pacote twine: python3 -m pip install --upgrade twine
  2. Upload para PYPI test env: python3 -m twine upload --repository testpypi dist/*
  3. Fazer upload para PYPI: python3 -m twine upload dist/*

CI/CD por fluxos de trabalho do Github

Consultar: https://packaging.python.org/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/

Queremos uma maneira de fazer upload de ativos automaticamente, ele pode construir os pacotes e enviá-los para lançamentos do github e PYPI.

(Por alguma razão, queremos apenas que o fluxo de trabalho publique o lançamento para testar o 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

Saiba mais sobre o Milvus

Milvus é uma ferramenta poderosa capaz de alimentar uma vasta gama de aplicações de inteligência artificial e pesquisa de similaridade vetorial. Para saber mais sobre o projeto, consulte os seguintes recursos:

  • Leia o nosso blogue.
  • Interagir com a nossa comunidade de código aberto no Slack.
  • Utilize ou contribua para a base de dados de vectores mais popular do mundo no GitHub.
  • Teste e implemente rapidamente aplicações de IA com o nosso novo bootcamp.

Like the article? Spread the word

Continue Lendo