🚀 Prueba Zilliz Cloud, el Milvus completamente gestionado, gratis—¡experimenta un rendimiento 10 veces más rápido! Prueba Ahora>>

milvus-logo
LFAI

Visión general

  • Engineering
September 15, 2021
Zhen Chen

Visión general

URL del proyecto: https://github.com/milvus-io/milvus_cli

Preparación: Python3.8, Click 8.0.x

Agrupar comandos

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

Como en el código anterior, usamos @click.group() para crear un grupo de comandos cli como punto de entrada. Para implementar un prompt CLI necesitamos deshabilitar los mensajes de ayuda para la entrada, así que añadimos no_args_is_help=False, add_help_option=False y invoke_without_command=True. Y no se imprimirá nada si introducimos cli sólo en el terminal.

Además usamos @click.pass_context para pasar un contexto a este grupo para su uso posterior.

Crear un subcomando del grupo de comandos

Luego agregamos el primer sub comando help bajo 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))

Ahora podemos usar cli help en la terminal:

$ python milvus_cli/scripts/milvus_cli.py help

Crear un subgrupo de un grupo de comandos

No sólo queremos tener un subcomando como cli help, sino que también necesitamos un subgrupo de comandos como cli list collection, cli list partition y cli list indexes.

Primero creamos un comando de subgrupo list, aquí podemos pasar el primer parámetro a @cli.group como el nombre del comando en lugar de usar el nombre de la función predeterminada, así podemos reducir la duplicación de nombres de funciones.

Atención aquí, usamos @cli.group() en lugar de @click.group para crear un subgrupo del grupo de origen.

Usamos @click.pass_obj para pasar context.obj a los subcomandos de este subgrupo.

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

Luego agregamos algunos sub comandos en este sub grupo por @listDetails.command() (no @cli.command()). Esto es solo un ejemplo, puedes ignorar el implemento y lo discutiremos mas 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)

Después de todo esto completo, tenemos un miltigroup comandos que se parecen:

image imagen

Personalizar un comando

Añadir opciones

Puede añadir algunas opciones a un comando que se utilizará como cli --test-option value.

Aquí hay un ejemplo, agregamos tres opciones alias, host y port para especificar una dirección para conectarse a Milvus.

Los dos primeros parámetros definen el nombre corto y completo de la opción, el tercer parámetro define el nombre de la variable, el parámetro help especifica el mensaje corto de ayuda, el parámetro default especifica el valor por defecto y el type especifica el tipo de valor.

Y todos los valores de las opciones se pasarán a la función por orden de definición.

@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

Añadir opciones de bandera

Usamos las opciones anteriores para pasar un valor, pero algunas veces sólo necesitamos una bandera como valor booleano.

Como en el ejemplo de abajo, la opción autoId es una opción de bandera y no pasa ningún dato a la función, así que podemos usarla 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

Añadir argumentos

En este proyecto reemplazamos el uso de argumentos por el uso de opciones. Pero todavía introducimos el uso de argumentos aquí. A diferencia de las opciones, los argumentos se utilizan como cli COMMAND [OPTIONS] ARGUEMENTS. Si convertimos el ejemplo anterior en el uso de argumentos, será así:

@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

Entonces el uso debería ser cli create collection c_name -p p_name -a.

Añadir mensaje de ayuda completo

Así como definimos el mensaje de ayuda corto arriba, podemos definir el mensaje de ayuda completo en la función:

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

El primer bloque dentro de la función es el mensaje de ayuda que se imprimirá después de introducir 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.

Añadir confirmación

A veces necesitamos que el usuario confirme alguna acción, especialmente borrar algo. Podemos añadir click.confirm para pausar y pedir al usuario que confirme:

@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 en el ejemplo anterior, una conversación de confirmación se mostrará como Aborted!ant to continue? [y/N]:.

Añadir avisos

Para implementar avisos sólo necesitamos añadir 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

El aviso se mostrará cada vez que click.prompt. Utilizaremos varios avisos en serie para que parezca una conversación continua. Esto asegura que el usuario introducirá los datos en el orden que queremos. En este caso necesitamos que el usuario elija una colección primero, y necesitamos obtener todas las particiones bajo esta colección, luego mostrárselas al usuario para que elija.

Añadir opciones

A veces queremos que el usuario sólo introduzca un rango limitado/tipo de valor, podemos añadir type=click.Choice([<any>]) a click.prompt, click.options y etc...

Por ejemplo,

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

Entonces el usuario solo puede ingresar collection_1 o collection_2, se producirá un error si ingresa cualquier otro valor.

Añadir pantalla clara

Puede utilizar click.clear() para implementarlo.

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

Consejos adicionales

  • El valor por defecto es None, por lo que no tiene sentido si se especifica el valor por defecto como None. Y por defecto None causará click.prompt continuamente mostrar si desea dejar un valor vacío para saltar por encima de ella.

Implementar prompt CLI para que el usuario introduzca

Por qué prompt CLI

Para el funcionamiento de la base de datos, necesitamos una conexión continua a una instancia. Si utilizamos el modo de línea de comandos de origen, la conexión se caerá después de cada comando ejecutado. También queremos almacenar algunos datos cuando se utiliza CLI, y limpiarlos después de la salida.

Implementar

  1. Utilice while True para escuchar continuamente la entrada del usuario.
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. El uso de input sólo causará up, down, left, right teclas de flecha, tab tecla y algunas otras teclas convertidas a cadena Acsii automáticamente. Además, los comandos del historial no se pueden leer desde la sesión. Asi que agregamos readline a la funcion 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. Agregue 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. Captura KeyboardInterrupt error cuando se utiliza ctrl C para salir.
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. Despues de todo arreglado, el CLI ahora se ve asi:
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 autocompletar

A diferencia de click's shell autocomplete, nuestro proyecto envuelve la linea de comandos y usa un bucle para obtener la entrada del usuario para implementar una linea de comandos prompt. Así que necesitamos enlazar un completador 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]

Después de definir Completer podemos enlazarlo con 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)

Añadir opción única

Para la línea de comandos prompt, a veces no queremos ejecutar completamente los scripts para obtener alguna información como la versión. Un buen ejemplo es Python, cuando escribes python en la terminal la línea de comandos promtp se mostrará, pero sólo devuelve un mensaje de versión y no entrará en los scripts prompt si escribes python -V. Así que podemos usar sys.args en nuestro código para implementarlo.

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

Obtenemos sys.args antes del bucle cuando se ejecuta por primera vez en las secuencias de comandos CLI. Si el último argumento es --version, el código devolverá la versión del paquete sin entrar en el bucle.

Será útil después de construir los códigos como un paquete. El usuario puede escribir milvus_cli para saltar a un prompt CLI, o escribir milvus_cli --version para obtener sólo la versión.

Construir y liberar

Finalmente queremos construir un paquete y liberarlo mediante PYPI. Así que el usuario puede simplemente utilizar pip install <package name> para instalar.

Instalar localmente para probar

Antes de publicar el paquete en PYPI, es posible que desee instalarlo localmente para realizar algunas pruebas.

En este caso, puede simplemente cd en el directorio del paquete y ejecutar pip install -e . (No olvide el .).

Crear los archivos del paquete

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

La estructura de un paquete debería ser la siguiente

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

Crear el directorio del paquete

Cree el directorio Milvus_cli con la siguiente estructura:

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

Escriba el código de entrada

La entrada del script debe estar en Milvus_cli/milvus_cli/scripts, y el Milvus_cli/milvus_cli/scripts/milvus_cli.py debe 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 el 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'
)

Algunos consejos aquí:

  1. Usamos el contenido de README.md como descripción larga del paquete.
  2. Añade todas las dependencias a install_requires.
  3. Especifique entry_points. En este caso, establecemos milvus_cli como hijo de console_scripts, para que podamos escribir milvus_cli como comando directamente después de instalar este paquete. Y el punto de entrada de milvus_cli es la función runCliPrompt en milvus_cli/scripts/milvus_cli.py.

Construir

  1. Actualiza el paquete build: python3 -m pip install --upgrade build

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

  3. Se generarán dos archivos en el directorio 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 versión

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

  1. Actualice el paquete twine: python3 -m pip install --upgrade twine
  2. Subir a PYPI env. de prueba: python3 -m twine upload --repository testpypi dist/*
  3. Subir a PYPI: python3 -m twine upload dist/*

Flujos de trabajo CI/CD por Github

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

Queremos una manera de subir los activos de forma automática, puede construir los paquetes y subirlos a github libera y PYPI.

(Por alguna razón sólo queremos que el flujo de trabajo sólo publicar la versión para probar 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

Más información sobre Milvus

Milvus es una poderosa herramienta capaz de potenciar una amplia gama de aplicaciones de inteligencia artificial y búsqueda de similitud vectorial. Para saber más sobre el proyecto, consulte los siguientes recursos:

  • Lea nuestro blog.
  • Interactúe con nuestra comunidad de código abierto en Slack.
  • Utilice o contribuya a la base de datos vectorial más popular del mundo en GitHub.
  • Pruebe y despliegue rápidamente aplicaciones de IA con nuestro nuevo bootcamp.

Like the article? Spread the word

Sigue Leyendo