def test_tensorflow(integration_test_url, project_name, use_google_oauth): merlin.set_url(integration_test_url, use_google_oauth=use_google_oauth) merlin.set_project(project_name) merlin.set_model("tensorflow-sample", ModelType.TENSORFLOW) model_dir = "test/tensorflow-model" undeploy_all_version() with merlin.new_model_version() as v: merlin.log_model(model_dir=model_dir) endpoint = merlin.deploy(v) request_json = { "signature_name": "predict", "instances": [ { "sepal_length": 2.8, "sepal_width": 1.0, "petal_length": 6.8, "petal_width": 0.4, }, { "sepal_length": 0.1, "sepal_width": 0.5, "petal_length": 1.8, "petal_width": 2.4, }, ], } resp = requests.post(f"{endpoint.url}", json=request_json) assert resp.status_code == 200 assert resp.json() is not None assert len(resp.json()["predictions"]) == len(request_json["instances"]) merlin.undeploy(v)
def test_custom_model_without_artifact(integration_test_url, project_name, use_google_oauth): merlin.set_url(integration_test_url, use_google_oauth=use_google_oauth) merlin.set_project(project_name) merlin.set_model("custom-wo-artifact", ModelType.CUSTOM) undeploy_all_version() resource_request = ResourceRequest(1, 1, "1", "1Gi") with merlin.new_model_version() as v: v.log_custom_model(image="ghcr.io/tiopramayudi/custom-predictor:v0.2") endpoint = merlin.deploy(v, resource_request=resource_request) with open(os.path.join("test/custom-model", "input.json"), "r") as f: req = json.load(f) sleep(5) resp = requests.post(f"{endpoint.url}", json=req) assert resp.status_code == 200 assert resp.json() is not None assert resp.json()["predictions"] is not None model_endpoint = merlin.serve_traffic({endpoint: 100}) sleep(5) resp = requests.post(f"{model_endpoint.url}", json=req) assert resp.status_code == 200 assert resp.json() is not None assert resp.json()["predictions"] is not None # Try to undeploy serving model version. It must be fail with pytest.raises(Exception): assert merlin.undeploy(v) # Undeploy other running model version endpoints undeploy_all_version()
def test_mlflow_tracking(integration_test_url, project_name, use_google_oauth): merlin.set_url(integration_test_url, use_google_oauth=use_google_oauth) merlin.set_project(project_name) merlin.set_model("mlflow-tracking", ModelType.PYTORCH) model_dir = "test/pytorch-model" undeploy_all_version() with merlin.new_model_version() as v: merlin.log_pytorch_model(model_dir=model_dir) merlin.log_param("model_type", "pytorch") merlin.log_param("iteration", 5) merlin.set_tag("version", "v1.0") merlin.set_tag("build", "latest") merlin.set_tag("team_id", 1) merlin.log_metric("model_loaded", 10.23) assert merlin.get_param("model_type") == "pytorch" # Stringify value which is integer originally assert merlin.get_param("iteration") == "5" assert merlin.get_param("random_key") is None assert merlin.get_tag("version") == "v1.0" assert merlin.get_tag("xxx") is None # Stringify value which is integer originally assert merlin.get_tag("team_id") == "1" assert merlin.get_metric("model_loaded") == 10.23 assert merlin.get_metric("response_time") is None assert merlin.list_tag() == { "version": "v1.0", "build": "latest", "team_id": "1", }
def test_pyfunc_old_infer(integration_test_url, project_name, use_google_oauth): merlin.set_url(integration_test_url, use_google_oauth=use_google_oauth) merlin.set_project(project_name) merlin.set_model("pyfunc-old-infer-sample", ModelType.PYFUNC) undeploy_all_version() with merlin.new_model_version() as v: v.log_pyfunc_model(model_instance=OldInferModel(), conda_env="test/pyfunc/env.yaml", code_dir=["test"], artifacts={}) endpoint = merlin.deploy(v) resp = requests.post(f"{endpoint.url}", json=request_json) assert resp.status_code == 200 assert resp.json()['instances'] == request_json['instances'] merlin.undeploy(v) sleep(5) resp = requests.post(f"{endpoint.url}", json=request_json) assert resp.status_code == 404
def test_pyfunc_old_merlin_latest_mlflow(integration_test_url, project_name, use_google_oauth): merlin.set_url(integration_test_url, use_google_oauth=use_google_oauth) merlin.set_project(project_name) merlin.set_model("pf-old-merlin-new-mlflow", ModelType.PYFUNC) undeploy_all_version() with merlin.new_model_version() as v: iris = load_iris() y = iris['target'] X = iris['data'] xgb_path = train_xgboost_model(X, y) sklearn_path = train_sklearn_model(X, y) v.log_pyfunc_model( model_instance=EnsembleModel(), conda_env="test/pyfunc/env-old-merlin-latest-mlflow.yaml", code_dir=["test"], artifacts={ "xgb_model": xgb_path, "sklearn_model": sklearn_path }) endpoint = merlin.deploy(v) sleep(5) resp = requests.post(f"{endpoint.url}", json=request_json) assert resp.status_code == 200 assert resp.json() is not None assert len(resp.json()['predictions']) == len(request_json['instances']) merlin.undeploy(v) sleep(5) resp = requests.post(f"{endpoint.url}", json=request_json) assert resp.status_code == 404
def test_pytorch(integration_test_url, project_name, use_google_oauth): merlin.set_url(integration_test_url, use_google_oauth=use_google_oauth) merlin.set_project(project_name) merlin.set_model("pytorch-sample", ModelType.PYTORCH) model_dir = "test/pytorch-model" undeploy_all_version() with merlin.new_model_version() as v: merlin.log_pytorch_model(model_dir=model_dir) endpoint = merlin.deploy() resp = requests.post(f"{endpoint.url}", json=request_json) assert resp.status_code == 200 assert resp.json() is not None assert len(resp.json()['predictions']) == len(request_json['instances']) merlin.undeploy(v) sleep(5) resp = requests.post(f"{endpoint.url}", json=request_json) assert resp.status_code == 404
def test_tensorflow(integration_test_url, project_name, use_google_oauth): merlin.set_url(integration_test_url, use_google_oauth=use_google_oauth) merlin.set_project(project_name) merlin.set_model("tensorflow-sample", ModelType.TENSORFLOW) v = _get_latest_version(merlin.active_model()) port = _get_free_port() p = Process(target=v.start_server, kwargs={"port": port, "build_image": True}) p.start() _wait_server_ready(f"http://{host}:{port}/v1/models/{v.model.name}-{v.id}") request_json = { "signature_name": "predict", "instances": [ {"sepal_length": 2.8, "sepal_width": 1.0, "petal_length": 6.8, "petal_width": 0.4}, {"sepal_length": 0.1, "sepal_width": 0.5, "petal_length": 1.8, "petal_width": 2.4} ] } resp = requests.post(_get_local_endpoint(v, port), json=request_json) assert resp.status_code == 200 assert resp.json() is not None assert len(resp.json()['predictions']) == len(request_json['instances']) p.terminate()
def test_set_model(url, project, model, mock_oauth, use_google_oauth): # expect exception when setting model but client and project is not set with pytest.raises(Exception): merlin.set_model(model.name, model.type) merlin.set_url(url, use_google_oauth=use_google_oauth) with pytest.raises(Exception): merlin.set_model(model.name, model.type) _mock_get_project_call(project) merlin.set_project(project.name) _mock_get_model_call(project, model) merlin.set_model(model.name, model.type) assert merlin.active_model().name == model.name assert merlin.active_model().type == model.type assert merlin.active_model().id == model.id assert merlin.active_model( ).mlflow_experiment_id == model.mlflow_experiment_id
def test_cli_deployment_undeployment_with_resource_request( deployment_info, runner, use_google_oauth): model_name = 'cli-resource-request-test' merlin.set_url(deployment_info['url'], use_google_oauth=use_google_oauth) merlin.set_project(deployment_info['project']) merlin.set_model(model_name, ModelType.SKLEARN) undeploy_all_version() # Deployment result = runner.invoke(cli, [ 'deploy', '--env', deployment_info['env'], '--model-type', deployment_info['model_type'], '--model-dir', deployment_info['model_dir'], '--model-name', model_name, '--project', deployment_info['project'], '--url', deployment_info['url'], '--min-replica', deployment_info['min_replica'], '--max-replica', deployment_info['max_replica'], '--cpu-request', deployment_info['cpu_request'], '--memory-request', deployment_info['memory_request'], ]) if result.exception: traceback.print_exception(*result.exc_info) test_deployed_model_version = result.output.split('\n')[0].split(' ')[-1] # Get latest deployed model's version merlin.set_url(deployment_info['url'], use_google_oauth=use_google_oauth) merlin.set_project(deployment_info['project']) merlin.set_model(model_name, ModelType.SKLEARN) merlin_active_model = merlin.active_model() all_versions = merlin_active_model.list_version() latest_version = all_versions[0] # Undeployment undeploy_result = runner.invoke(cli, [ 'undeploy', '--model-version', test_deployed_model_version, '--model-name', model_name, '--project', deployment_info['project'], '--url', deployment_info['url'] ]) if result.exception: traceback.print_exception(*result.exc_info) planned_output = "Deleting deployment of model {} version {}".format( model_name, test_deployed_model_version) received_output = undeploy_result.output.split(' from')[0] assert latest_version._id == int(test_deployed_model_version) assert received_output == planned_output
def test_standard_transformer_with_multiple_feast_with_source( integration_test_url, project_name, use_google_oauth, feast_serving_bigtable_url, ): merlin.set_url(integration_test_url, use_google_oauth=use_google_oauth) merlin.set_project(project_name) merlin.set_model("std-trf-feasts-source", ModelType.PYFUNC) undeploy_all_version() with merlin.new_model_version() as v: v.log_pyfunc_model( model_instance=EchoModel(), conda_env="test/pyfunc/env.yaml", code_dir=["test"], artifacts={}, ) config_template_file_path = os.path.join( "test/transformer", "standard_transformer_feast_with_source.yaml.tmpl") config_file_path = os.path.join( "test/transformer", "standard_transformer_multiple_feast.yaml") from string import Template config_template_file = open(config_template_file_path, "rt") t = Template(config_template_file.read()) rendered_config = t.substitute({ "feast_serving_bigtable_url": feast_serving_bigtable_url, }) config_file = open(config_file_path, "wt") config_file.write(rendered_config) config_file.close() env_vars = { "FEAST_REDIS_DIRECT_STORAGE_ENABLED": True, "FEAST_REDIS_POOL_SIZE": 1, "FEAST_BIGTABLE_DIRECT_STORAGE_ENABLED": True, "FEAST_BIGTABLE_POOL_SIZE": 1, "FEAST_BIGTABLE_KEEP_ALIVE_INTERVAL": "2m", "FEAST_BIGTABLE_KEEP_ALIVE_TIMEOUT": "15s" } transformer = StandardTransformer(config_file=config_file_path, enabled=True, env_vars=env_vars) endpoint = merlin.deploy(v, transformer=transformer) request_json = { "drivers": [ { "id": "driver_1", "name": "driver-1" }, { "id": "driver_2", "name": "driver-2" }, ], "customer": { "id": 1111 }, } resp = requests.post(f"{endpoint.url}", json=request_json) assert resp.status_code == 200 assert resp.json() is not None exp_resp = { "instances": { "columns": [ "rank", "driver_id", "customer_id", "merlin_test_redis_driver_features:completion_rate", "merlin_test_redis_driver_features:cancellation_rate", "merlin_test_bt_driver_features:rating" ], "data": [[0, "driver_1", 1111, 0.85, 0.15, 4.2], [1, "driver_2", 1111, 0.6, 0.4, 4.2]] } } assert resp.json()["instances"] == exp_resp["instances"] merlin.undeploy(v)