예제 #1
0
def read_image_from_url_cv2(url):
    import_cv2()
    logger.info("Read cv2 image from: {0}".format(url))
    return cv2.imdecode(
        np.asarray(bytearray(
            urlopen(Request(url=url, headers=headers), timeout=100).read()),
                   dtype="uint8"), cv2.IMREAD_UNCHANGED)
예제 #2
0
 def upload_bytes(self,
                  bytes_data,
                  key,
                  bucket_name=None,
                  overwrite=False,
                  encrypt=False,
                  acl=None):
     """
     Uploads bytes to S3
     This is provided as a convenience to drop a string in S3. It uses the
     boto infrastructure to ship a file to s3.
     Args:
         bytes_data: `bytes`. bytes to set as content for the key.
         key: `str`. S3 key that will point to the file.
         bucket_name: `str`. Name of the bucket in which to store the file.
         overwrite: `bool`. A flag to decide whether or not to overwrite the key
             if it already exists.
         encrypt: `bool`. If True, the file will be encrypted on the server-side
             by S3 and will be stored in an encrypted form while at rest in S3.
         acl: `str`. ACL to use for uploading, e.g. "public-read".
     """
     if not bucket_name:
         bucket_name = self.bucket
     if bucket_name is None:
         raise MlChainError("bucket can't be None",
                            code="S000",
                            status_code=500)
     filelike_buffer = BytesIO(bytes_data)
     try:
         self.client.upload_fileobj(filelike_buffer, bucket_name, key)
     except Exception as ex:
         logger.info(str(ex))
         raise MlChainError(str(ex), code="S006", status_code=500)
예제 #3
0
            def load(self):
                original_cuda_variable = os.environ.get('CUDA_VISIBLE_DEVICES')
                if original_cuda_variable is None:
                    os.environ['CUDA_VISIBLE_DEVICES'] = str(next(gpus))
                else:
                    logger.info(
                        f"Skipping automatic GPU selection for gunicorn worker since CUDA_VISIBLE_DEVICES environment variable is already set to {original_cuda_variable}"
                    )
                serve_model = get_model(entry_file, serve_model=True)

                if serve_model is None:
                    raise Exception(
                        f"Can not init model class from {entry_file}. Please check mlconfig.yaml or {entry_file} or mlchain run -m {{mode}}!"
                    )

                if isinstance(serve_model, ServeModel):
                    if (not self.autofrontend) and model_id is not None:
                        from mlchain.server.autofrontend import register_autofrontend
                        register_autofrontend(model_id,
                                              serve_model=serve_model,
                                              version=version,
                                              endpoint=os.getenv('NGROK_URL'))
                        self.autofrontend = True

                    if self.server == 'flask':
                        from mlchain.server.flask_server import FlaskServer
                        app = FlaskServer(serve_model,
                                          name=name,
                                          api_format=api_format,
                                          version=version,
                                          authentication=authentication,
                                          static_url_path=static_url_path,
                                          static_folder=static_folder,
                                          template_folder=template_folder)
                        app.register_swagger()
                        if cors:
                            from flask_cors import CORS
                            CORS(app.app)
                        return app.app
                    if self.server == 'quart':
                        from mlchain.server.quart_server import QuartServer
                        app = QuartServer(serve_model,
                                          name=name,
                                          api_format=api_format,
                                          version=version,
                                          authentication=authentication,
                                          static_url_path=static_url_path,
                                          static_folder=static_folder,
                                          template_folder=template_folder)
                        app.register_swagger()
                        if cors:
                            from quart_cors import cors as CORS
                            CORS(app.app)
                        return app.app
                return None
예제 #4
0
 def delete(self, path, bucket=None):
     bucket = bucket or self.bucket
     if bucket is None:
         raise MlChainError("bucket can't be None",
                            code="S000",
                            status_code=500)
     try:
         self.client.remove_object(bucket, path)
     except Exception as ex:
         logger.info(str(ex))
         raise MlChainError(str(ex), code="S011", status_code=500)
예제 #5
0
 def listdir(self, path, bucket=None):
     bucket = bucket or self.bucket
     if bucket is None:
         raise MlChainError("bucket can't be None",
                            code="S000",
                            status_code=500)
     try:
         results = self.list(bucket_name=bucket, prefix=path)
         return {'files': results['keys'], 'dirs': results['prefixes']}
     except Exception as ex:
         logger.info(str(ex))
         raise MlChainError(str(ex), code="S010", status_code=500)
예제 #6
0
def register_autofrontend(model_id,
                          serve_model,
                          version='latest',
                          endpoint=None):
    mlchain_management = os.getenv('MLCHAIN_URL', None)
    if endpoint is None:
        endpoint = ''
    autofrontend_template = AutofrontendConfig()
    if serve_model.config is not None:
        out_configs = serve_model.config
    else:
        out_configs = {}
    for name, func in serve_model.get_all_func().items():
        if name in out_configs:
            out_config = out_configs[name]
            if 'config' in out_config:
                config = out_config['config']
            else:
                config = None
        else:
            config = None
        autofrontend_template.add_endpoint(func,
                                           f'{endpoint}/call/{name}',
                                           output_config=config)
    if os.path.exists("Readme.md"):
        description = open("Readme.md", encoding='utf-8').read()
    else:
        description = ""

    if os.path.exists("changelog.md"):
        changelog = open("changelog.md", encoding='utf-8').read()
    else:
        changelog = ""

    if mlchain_management and model_id is not None:
        config_version = {
            "model_id": model_id,
            "version": version,
            "input_config": autofrontend_template.input_config,
            "output_config": autofrontend_template.output_config,
            'endpoint': endpoint,
            'readme': description,
            'changelog': changelog
        }
        try:
            import requests
            res = requests.post(f'{mlchain_management}/version/create',
                                json=config_version)
            logger.info(str(res.json()))
        except Exception as ex:
            logger.error(ex)
예제 #7
0
    def upload_cv2(self,
                   img,
                   key,
                   bucket_name=None,
                   overwrite=False,
                   quality=95,
                   img_fmt='.jpg',
                   acl=None):
        """
        Uploads bytes to S3
        This is provided as a convenience to drop a string in S3. It uses the
        boto infrastructure to ship a file to s3.
        Args:
            bytes_data: `bytes`. bytes to set as content for the key.
            key: `str`. S3 key that will point to the file.
            bucket_name: `str`. Name of the bucket in which to store the file.
            overwrite: `bool`. A flag to decide whether or not to overwrite the key
                if it already exists.
            encrypt: `bool`. If True, the file will be encrypted on the server-side
                by S3 and will be stored in an encrypted form while at rest in S3.
            acl: `str`. ACL to use for uploading, e.g. "public-read".
        """
        if not bucket_name:
            bucket_name = self.bucket
        if bucket_name is None:
            raise MlChainError("bucket can't be None",
                               code="S000",
                               status_code=500)
        try:
            jpg_formats = ['.JPG', '.JPEG']
            png_formats = ['.PNG']
            encode_params = None
            img_fmt = '.{0}'.format(img_fmt.strip('.')).upper()
            cv2 = import_cv2()
            if img_fmt in jpg_formats:
                encode_params = [cv2.IMWRITE_JPEG_QUALITY, quality]
            elif img_fmt in png_formats:
                encode_params = [cv2.IMWRITE_PNG_COMPRESSION, quality]
            ret, buf = cv2.imencode(img_fmt, img, encode_params)

            filelike_buffer = BytesIO(buf.tostring())

            self.client.upload_fileobj(filelike_buffer, bucket_name, key)
        except Exception as ex:
            logger.info(str(ex))
            raise MlChainError(str(ex), code="S007", status_code=500)
예제 #8
0
 def upload_dir(self, dir_path, prefix, bucket_name=None):
     bucket_name = bucket_name or self.bucket
     if bucket_name is None:
         raise MlChainError("bucket can't be None",
                            code="S000",
                            status_code=500)
     try:
         for file in glob.glob(os.path.join(dir_path, '*'), recursive=True):
             file_name = file[len(dir_path):].replace('\\', '/')
             if file_name.startswith('/'):
                 file_name = file_name[1:]
             key = os.path.join(prefix, file_name).replace('\\', '/')
             if os.path.isfile(file):
                 self.upload_file(file, key, bucket_name)
             else:
                 self.upload_dir(file, key, bucket_name)
     except Exception as ex:
         logger.info(str(ex))
         raise MlChainError(str(ex), code="S008", status_code=500)
예제 #9
0
def read_image_from_url_pil(url):
    logger.info("Read pil image from: {0}".format(url))
    file = BytesIO(
        urlopen(Request(url=url, headers=headers), timeout=100).read())
    img = Image.open(file)
    return img
예제 #10
0
def run_command(entry_file, host, port, bind, wrapper, server, workers, config,
                name, mode, api_format, ngrok, kws):
    kws = list(kws)
    if isinstance(entry_file, str) and not os.path.exists(entry_file):
        kws = [f'--entry_file={entry_file}'] + kws
        entry_file = None
    from mlchain import config as mlconfig
    default_config = False

    if config is None:
        default_config = True
        config = 'mlconfig.yaml'

    config_path = copy.deepcopy(config)
    if os.path.isfile(config_path) and os.path.exists(config_path):
        config = mlconfig.load_file(config_path)
        if config is None:
            raise SystemExit(
                "Config file {0} are not supported".format(config_path))
    else:
        if not default_config:
            raise SystemExit("Can't find config file {0}".format(config_path))
        else:
            raise SystemExit(
                "Can't find mlchain config file. Please double check your current working directory. Or use `mlchain init` to initialize a new ones here."
            )
    if 'mode' in config and 'env' in config['mode']:
        if mode in config['mode']['env']:
            config['mode']['default'] = mode
        elif mode is not None:
            available_mode = list(config['mode']['env'].keys())
            available_mode = [
                each for each in available_mode if each != 'default'
            ]
            raise SystemExit(
                f"No {mode} mode are available. Found these mode in config file: {available_mode}"
            )
    mlconfig.load_config(config)
    for kw in kws:
        if kw.startswith('--'):
            tokens = kw[2:].split('=', 1)
            if len(tokens) == 2:
                key, value = tokens
                mlconfig.mlconfig.update({key: value})
            else:
                raise AssertionError("Unexpected param {0}".format(kw))
        else:
            raise AssertionError("Unexpected param {0}".format(kw))
    model_id = mlconfig.get_value(None, config, 'model_id', None)
    entry_file = mlconfig.get_value(entry_file, config, 'entry_file',
                                    'server.py')
    if entry_file.strip() == '':
        raise SystemExit(f"Entry file cannot be empty")
    if not os.path.exists(entry_file):
        raise SystemExit(
            f"Entry file {entry_file} not found in current working directory.")
    host = mlconfig.get_value(host, config, 'host', 'localhost')
    port = mlconfig.get_value(port, config, 'port', 5000)
    server = mlconfig.get_value(server, config, 'server', 'flask')
    if len(bind) == 0:
        bind = None
    bind = mlconfig.get_value(bind, config, 'bind', [])
    wrapper = mlconfig.get_value(wrapper, config, 'wrapper', None)
    if wrapper == 'gunicorn' and os.name == 'nt':
        logger.warning(
            'Gunicorn warper are not supported on Windows. Switching to None instead.'
        )
        wrapper = None
    workers = None
    if 'gunicorn' in config:
        workers = mlconfig.get_value(workers, config['gunicorn'], 'workers',
                                     None)
    if workers is None and 'hypercorn' in config.keys():
        workers = mlconfig.get_value(workers, config['hypercorn'], 'workers',
                                     None)
    workers = int(workers) if workers is not None else 1
    name = mlconfig.get_value(name, config, 'name', None)
    cors = mlconfig.get_value(None, config, 'cors', False)

    static_folder = mlconfig.get_value(None, config, 'static_folder', None)
    static_url_path = mlconfig.get_value(None, config, 'static_url_path', None)
    template_folder = mlconfig.get_value(None, config, 'template_folder', None)

    version = mlconfig.get_value(None, config, 'version', '0.0')
    version = str(version)
    api_format = mlconfig.get_value(api_format, config, 'api_format', None)
    api_keys = os.getenv('API_KEYS', None)
    if api_keys is not None:
        api_keys = api_keys.split(';')
    api_keys = mlconfig.get_value(api_keys, config, 'api_keys', None)
    if api_keys is None:
        authentication = None
    else:
        authentication = Authentication(api_keys)
    import logging
    logging.root = logging.getLogger(name)
    logger.debug(
        dict(entry_file=entry_file,
             host=host,
             port=port,
             bind=bind,
             wrapper=wrapper,
             server=server,
             workers=workers,
             name=name,
             mode=mode,
             api_format=api_format,
             kws=kws))
    bind = list(bind)
    if ngrok:
        from pyngrok import ngrok as pyngrok
        endpoint = pyngrok.connect(port=port)
        logger.info("Ngrok url: {0}".format(endpoint))
        os.environ['NGROK_URL'] = endpoint
    if server == 'grpc':
        from mlchain.server.grpc_server import GrpcServer
        app = get_model(entry_file, serve_model=True)

        if app is None:
            raise Exception(
                "Can not init model class from {0}. Please check mlconfig.yaml or {0} or mlchain run -m {{mode}}!"
                .format(entry_file))

        app = GrpcServer(app, name=name)
        app.run(host, port)
    elif wrapper == 'gunicorn':
        from gunicorn.app.base import BaseApplication
        gpus = select_gpu()

        class GunicornWrapper(BaseApplication):
            def __init__(self, server_, **kwargs):
                assert server_.lower() in ['quart', 'flask']
                self.server = server_.lower()
                self.options = kwargs
                self.autofrontend = False
                super(GunicornWrapper, self).__init__()

            def load_config(self):
                config = {
                    key: value
                    for key, value in self.options.items()
                    if key in self.cfg.settings and value is not None
                }
                for key, value in config.items():
                    self.cfg.set(key.lower(), value)

                from mlchain.base.gunicorn_config import post_worker_init
                self.cfg.set("post_worker_init", post_worker_init)

            def load(self):
                original_cuda_variable = os.environ.get('CUDA_VISIBLE_DEVICES')
                if original_cuda_variable is None:
                    os.environ['CUDA_VISIBLE_DEVICES'] = str(next(gpus))
                else:
                    logger.info(
                        f"Skipping automatic GPU selection for gunicorn worker since CUDA_VISIBLE_DEVICES environment variable is already set to {original_cuda_variable}"
                    )
                serve_model = get_model(entry_file, serve_model=True)

                if serve_model is None:
                    raise Exception(
                        f"Can not init model class from {entry_file}. Please check mlconfig.yaml or {entry_file} or mlchain run -m {{mode}}!"
                    )

                if isinstance(serve_model, ServeModel):
                    if (not self.autofrontend) and model_id is not None:
                        from mlchain.server.autofrontend import register_autofrontend
                        register_autofrontend(model_id,
                                              serve_model=serve_model,
                                              version=version,
                                              endpoint=os.getenv('NGROK_URL'))
                        self.autofrontend = True

                    if self.server == 'flask':
                        from mlchain.server.flask_server import FlaskServer
                        app = FlaskServer(serve_model,
                                          name=name,
                                          api_format=api_format,
                                          version=version,
                                          authentication=authentication,
                                          static_url_path=static_url_path,
                                          static_folder=static_folder,
                                          template_folder=template_folder)
                        app.register_swagger()
                        if cors:
                            from flask_cors import CORS
                            CORS(app.app)
                        return app.app
                    if self.server == 'quart':
                        from mlchain.server.quart_server import QuartServer
                        app = QuartServer(serve_model,
                                          name=name,
                                          api_format=api_format,
                                          version=version,
                                          authentication=authentication,
                                          static_url_path=static_url_path,
                                          static_folder=static_folder,
                                          template_folder=template_folder)
                        app.register_swagger()
                        if cors:
                            from quart_cors import cors as CORS
                            CORS(app.app)
                        return app.app
                return None

        if host is not None and port is not None:
            bind.append('{0}:{1}'.format(host, port))

        bind = list(set(bind))
        gunicorn_config = config.get('gunicorn', {})
        gunicorn_env = ['worker_class', 'threads', 'workers']
        if workers is not None:
            gunicorn_config['workers'] = workers

        for k in gunicorn_env:
            if get_env(k) in os.environ:
                gunicorn_config[k] = os.environ[get_env(k)]
        if server == 'flask' and 'worker_class' in gunicorn_config:
            if 'uvicorn' in gunicorn_config['worker_class']:
                logger.warning(
                    "Can't use flask with uvicorn. change to gthread")
                gunicorn_config['worker_class'] = 'gthread'

        GunicornWrapper(server, bind=bind, **gunicorn_config).run()
    elif wrapper == 'hypercorn' and server == 'quart':
        from mlchain.server.quart_server import QuartServer
        app = get_model(entry_file, serve_model=True)

        if app is None:
            raise Exception(
                "Can not init model class from {0}. Please check mlconfig.yaml or {0} or mlchain run -m {{mode}}!"
                .format(entry_file))

        app = QuartServer(app,
                          name=name,
                          version=version,
                          api_format=api_format,
                          authentication=authentication,
                          static_url_path=static_url_path,
                          static_folder=static_folder,
                          template_folder=template_folder)
        app.run(host,
                port,
                bind=bind,
                cors=cors,
                gunicorn=False,
                hypercorn=True,
                **config.get('hypercorn', {}),
                model_id=model_id)

    app = get_model(entry_file)

    if app is None:
        raise Exception(
            "Can not init model class from {0}. Please check mlconfig.yaml or {0} or mlchain run -m {{mode}}!"
            .format(entry_file))

    if isinstance(app, MLServer):
        if app.__class__.__name__ == 'FlaskServer':
            app.run(host, port, cors=cors, gunicorn=False)
        elif app.__class__.__name__ == 'QuartServer':
            app.run(host, port, cors=cors, gunicorn=False, hypercorn=False)
        elif app.__class__.__name__ == 'GrpcServer':
            app.run(host, port)
    elif isinstance(app, ServeModel):
        if server not in ['quart', 'grpc']:
            server = 'flask'
        if server == 'flask':
            from mlchain.server.flask_server import FlaskServer
            app = FlaskServer(app,
                              name=name,
                              api_format=api_format,
                              version=version,
                              authentication=authentication,
                              static_url_path=static_url_path,
                              static_folder=static_folder,
                              template_folder=template_folder)
            app.run(host,
                    port,
                    cors=cors,
                    gunicorn=False,
                    model_id=model_id,
                    threads=workers > 1)
        elif server == 'quart':
            from mlchain.server.quart_server import QuartServer
            app = QuartServer(app,
                              name=name,
                              api_format=api_format,
                              version=version,
                              authentication=authentication,
                              static_url_path=static_url_path,
                              static_folder=static_folder,
                              template_folder=template_folder)
            app.run(host,
                    port,
                    cors=cors,
                    gunicorn=False,
                    hypercorn=False,
                    model_id=model_id,
                    workers=workers)

        elif server == 'grpc':
            from mlchain.server.grpc_server import GrpcServer
            app = GrpcServer(app, name=name)
            app.run(host, port)
예제 #11
0
    def list(self,
             bucket_name=None,
             prefix='',
             delimiter='/',
             page_size=None,
             max_items=None,
             keys=True,
             prefixes=True):
        """
        Lists prefixes and contents in a bucket under prefix.
        Args:
            bucket_name: `str`. the name of the bucket
            prefix: `str`. a key prefix
            delimiter: `str`. the delimiter marks key hierarchy.
            page_size: `str`. pagination size
            max_items: `int`. maximum items to return
            keys: `bool`. if it should include keys
            prefixes: `boll`. if it should include prefixes
        """
        if not bucket_name:
            bucket_name = self.bucket
        if bucket_name is None:
            raise MlChainError("bucket can't be None",
                               code="S000",
                               status_code=500)
        config = {
            'PageSize': page_size,
            'MaxItems': max_items,
        }
        try:
            paginator = self.client.get_paginator('list_objects_v2')

            response = paginator.paginate(Bucket=bucket_name,
                                          Prefix=prefix,
                                          Delimiter=delimiter,
                                          PaginationConfig=config)

            def get_keys(contents):
                list_keys = []
                for cont in contents:
                    list_keys.append(cont['Key'][len(prefix):])

                return list_keys

            def get_prefixes(page_prefixes):
                list_prefixes = []
                for pref in page_prefixes:
                    list_prefixes.append(pref['Prefix'][len(prefix):-1])
                return list_prefixes

            results = {'keys': [], 'prefixes': []}
            for page in response:
                if prefixes:
                    results['prefixes'] += get_prefixes(
                        page.get('CommonPrefixes', []))
                if keys:
                    results['keys'] += get_keys(page.get('Contents', []))

            return results
        except Exception as ex:
            logger.info(str(ex))
            raise MlChainError(str(ex), code="S009", status_code=500)