├── .dockerignore ├── .gitbook.yaml ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── pull_request_template.md ├── release.yml └── workflows │ ├── external.yml │ ├── merlin.yml │ ├── pr-checks.yaml │ ├── release.yml │ └── trigger-caraml-doc-sync.yml ├── .gitignore ├── .readthedocs.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── Makefile ├── OWNERS ├── README.md ├── api ├── .gitignore ├── .golangci.yml ├── README.md ├── api │ ├── deployment_api.go │ ├── deployment_api_test.go │ ├── environment_api.go │ ├── environment_api_test.go │ ├── log_api.go │ ├── model_endpoint_alerts_api.go │ ├── model_endpoint_alerts_api_test.go │ ├── model_endpoints_api.go │ ├── model_endpoints_api_test.go │ ├── model_schema_api.go │ ├── model_schema_api_test.go │ ├── models_api.go │ ├── models_api_test.go │ ├── prediction_job_api.go │ ├── prediction_job_api_test.go │ ├── projects_api.go │ ├── projects_api_test.go │ ├── response.go │ ├── response_test.go │ ├── router.go │ ├── router_test.go │ ├── secrets_api.go │ ├── secrets_api_test.go │ ├── transformer_api.go │ ├── transformer_api_test.go │ ├── utils.go │ ├── validator.go │ ├── version_endpoints_api.go │ ├── version_endpoints_api_test.go │ ├── version_image_api.go │ ├── version_image_api_test.go │ ├── versions_api.go │ └── versions_api_test.go ├── batch │ ├── controller.go │ ├── controller_test.go │ ├── manifest.go │ ├── manifest_test.go │ ├── mocks │ │ ├── controller.go │ │ └── manifest_manager.go │ ├── resource.go │ └── resource_test.go ├── client │ ├── api_endpoint.go │ ├── api_environment.go │ ├── api_log.go │ ├── api_model_endpoints.go │ ├── api_model_schema.go │ ├── api_models.go │ ├── api_prediction_jobs.go │ ├── api_project.go │ ├── api_secret.go │ ├── api_standard_transformer.go │ ├── api_version.go │ ├── api_version_image.go │ ├── client.go │ ├── configuration.go │ ├── examples │ │ ├── model-endpoints │ │ │ └── main.go │ │ └── project-endpoints │ │ │ └── main.go │ ├── model_alert_condition_metric_type.go │ ├── model_alert_condition_severity.go │ ├── model_autoscaling_policy.go │ ├── model_binary_classification_output.go │ ├── model_build_image_options.go │ ├── model_config.go │ ├── model_container.go │ ├── model_custom_predictor.go │ ├── model_deployment_mode.go │ ├── model_endpoint_status.go │ ├── model_env_var.go │ ├── model_environment.go │ ├── model_file_format.go │ ├── model_gpu_config.go │ ├── model_gpu_toleration.go │ ├── model_ground_truth_job.go │ ├── model_ground_truth_source.go │ ├── model_image_building_job_state.go │ ├── model_image_building_job_status.go │ ├── model_label.go │ ├── model_list_jobs_paginated_response.go │ ├── model_logger.go │ ├── model_logger_config.go │ ├── model_logger_mode.go │ ├── model_metrics_type.go │ ├── model_mock_response.go │ ├── model_model.go │ ├── model_model_endpoint.go │ ├── model_model_endpoint_alert.go │ ├── model_model_endpoint_alert_condition.go │ ├── model_model_endpoint_rule.go │ ├── model_model_endpoint_rule_destination.go │ ├── model_model_observability.go │ ├── model_model_prediction_config.go │ ├── model_model_prediction_output.go │ ├── model_model_prediction_output_class.go │ ├── model_model_schema.go │ ├── model_mounted_mlp_secret.go │ ├── model_operation_tracing.go │ ├── model_paging.go │ ├── model_pipeline_tracing.go │ ├── model_prediction_job.go │ ├── model_prediction_job_config.go │ ├── model_prediction_job_config_bigquery_sink.go │ ├── model_prediction_job_config_bigquery_source.go │ ├── model_prediction_job_config_gcs_sink.go │ ├── model_prediction_job_config_gcs_source.go │ ├── model_prediction_job_config_maxcompute_sink.go │ ├── model_prediction_job_config_maxcompute_source.go │ ├── model_prediction_job_config_model.go │ ├── model_prediction_job_config_model_result.go │ ├── model_prediction_job_resource_request.go │ ├── model_prediction_log_ingestion_resource_request.go │ ├── model_prediction_logger_config.go │ ├── model_project.go │ ├── model_protocol.go │ ├── model_ranking_output.go │ ├── model_regression_output.go │ ├── model_resource_request.go │ ├── model_result_type.go │ ├── model_save_mode.go │ ├── model_schema_spec.go │ ├── model_secret.go │ ├── model_standard_transformer_simulation_request.go │ ├── model_standard_transformer_simulation_response.go │ ├── model_transformer.go │ ├── model_value_type.go │ ├── model_version.go │ ├── model_version_endpoint.go │ ├── model_version_image.go │ ├── response.go │ └── utils.go ├── cluster │ ├── container.go │ ├── container_test.go │ ├── controller.go │ ├── controller_test.go │ ├── errors.go │ ├── job.go │ ├── job_test.go │ ├── labeller │ │ ├── labeller.go │ │ └── labeller_test.go │ ├── mocks │ │ ├── container_fetcher.go │ │ ├── controller.go │ │ └── namespace_creator.go │ ├── namespace.go │ ├── pdb.go │ ├── pdb_test.go │ ├── resource │ │ ├── templater.go │ │ ├── templater_gpu_test.go │ │ ├── templater_test.go │ │ └── utils.go │ ├── secret.go │ ├── virtual_service.go │ └── virtual_service_test.go ├── cmd │ ├── api │ │ ├── cronjob.go │ │ ├── main.go │ │ ├── setup.go │ │ └── ui_handler.go │ ├── inference-logger │ │ ├── Makefile │ │ ├── main.go │ │ └── main_test.go │ ├── mock-feast │ │ └── main.go │ └── transformer │ │ ├── .env.sample │ │ ├── Makefile │ │ └── main.go ├── config │ ├── batch.go │ ├── config.go │ ├── config_test.go │ ├── deployment.go │ ├── environment.go │ ├── environment_test.go │ ├── feast_storage_config.go │ ├── feast_storage_config_test.go │ ├── observability.go │ └── testdata │ │ ├── base-configs-1.yaml │ │ ├── bigtable-config.yaml │ │ ├── config-1.yaml │ │ ├── invalid-bigtable-config-no-instance.yaml │ │ ├── invalid-bigtable-config-no-pool-size.yaml │ │ ├── invalid-bigtable-config-no-project.yaml │ │ ├── invalid-bigtable-config-no-serving-url.yaml │ │ ├── invalid-duration-format.yaml │ │ ├── invalid-file-format.yaml │ │ ├── invalid-pdb-environment-both-nil.yaml │ │ ├── invalid-pdb-environment-both-set.yaml │ │ ├── invalid-redis-config-no-pool-size.yaml │ │ ├── invalid-redis-config-no-redis-addresses.yaml │ │ ├── invalid-redis-config-no-serving-url.yaml │ │ ├── invalid-type.yaml │ │ ├── redis-config.yaml │ │ ├── valid-environment-1.yaml │ │ ├── valid-imagebuilder-nodeselectors.yaml │ │ ├── valid-single-redis-cluster-config.yaml │ │ └── valid-single-redis-config.yaml ├── database │ ├── database.go │ ├── integration_config.go │ └── integration_database.go ├── go.mod ├── go.sum ├── istio │ ├── istio.go │ ├── istio_test.go │ └── mocks │ │ └── client.go ├── log │ └── log.go ├── middleware │ ├── authorization.go │ └── authorization_test.go ├── mlflow │ ├── error.go │ ├── mlflow.go │ ├── mlflow_test.go │ ├── mocks │ │ └── Client.go │ ├── response.go │ └── response_test.go ├── mlp │ ├── client.go │ ├── client_test.go │ ├── mocks │ │ └── APIClient.go │ ├── project.go │ ├── project_test.go │ ├── secret.go │ └── secret_test.go ├── models │ ├── container.go │ ├── container_test.go │ ├── deployment.go │ ├── endpoint_status.go │ ├── env_var.go │ ├── env_var_test.go │ ├── environment.go │ ├── gpu.go │ ├── gpu_test.go │ ├── logger.go │ ├── metadata.go │ ├── metadata_test.go │ ├── model.go │ ├── model_endpoint.go │ ├── model_endpoint_alert.go │ ├── model_endpoint_alert_test.go │ ├── model_observability.go │ ├── model_schema.go │ ├── observability_publisher.go │ ├── prediction_job.go │ ├── resource_config.go │ ├── resource_request.go │ ├── secret.go │ ├── service.go │ ├── service_test.go │ ├── transformer.go │ ├── version.go │ ├── version_endpoint.go │ ├── version_endpoint_test.go │ └── version_image.go ├── pkg │ ├── autoscaling │ │ ├── autoscaling.go │ │ └── autoscaling_test.go │ ├── cronjob │ │ ├── cronjob.go │ │ ├── cronjob_test.go │ │ ├── tracker.go │ │ └── tracker_test.go │ ├── deployment │ │ └── deployment_mode.go │ ├── errors │ │ └── errors.go │ ├── gitlab │ │ ├── gitlab.go │ │ ├── gitlab_test.go │ │ └── mocks │ │ │ └── gitlab.go │ ├── hystrix │ │ ├── client.go │ │ ├── client_test.go │ │ ├── logger.go │ │ ├── metrics.go │ │ ├── metrics_test.go │ │ └── util.go │ ├── imagebuilder │ │ ├── common.go │ │ ├── common_test.go │ │ ├── config.go │ │ ├── errors.go │ │ ├── imagebuilder.go │ │ ├── imagebuilder_test.go │ │ ├── janitor.go │ │ ├── janitor_test.go │ │ ├── mocks │ │ │ ├── image_builder.go │ │ │ └── name_generator.go │ │ ├── model_service_imagebuilder.go │ │ └── prediction_job_imagebuilder.go │ ├── inference-logger │ │ ├── liveness │ │ │ ├── handler.go │ │ │ └── handler_test.go │ │ ├── logger │ │ │ ├── console_sink.go │ │ │ ├── dispatcher.go │ │ │ ├── handler.go │ │ │ ├── handler_test.go │ │ │ ├── kafka_sink.go │ │ │ ├── kafka_sink_test.go │ │ │ ├── log_sink.go │ │ │ ├── mlobs_sink.go │ │ │ ├── mlobs_sink_test.go │ │ │ ├── newrelic_sink.go │ │ │ ├── queue.go │ │ │ ├── queue_test.go │ │ │ ├── types.go │ │ │ ├── types_test.go │ │ │ ├── worker.go │ │ │ └── worker_test.go │ │ ├── mock-server │ │ │ └── mock_server.go │ │ └── mocks │ │ │ ├── KafkaAdmin.go │ │ │ ├── KafkaProducer.go │ │ │ └── NewRelicLogsClient.go │ ├── kafka │ │ ├── config.go │ │ ├── kafka.go │ │ ├── kafka_test.go │ │ └── mocks │ │ │ └── producer.go │ ├── log │ │ ├── inference_log.pb.go │ │ └── inference_log.pb.json.go │ ├── observability │ │ ├── deployment │ │ │ ├── config.go │ │ │ ├── deployment.go │ │ │ ├── deployment_test.go │ │ │ ├── identity.go │ │ │ ├── identity_test.go │ │ │ ├── mocks │ │ │ │ └── deployer.go │ │ │ └── util.go │ │ └── event │ │ │ ├── event.go │ │ │ ├── event_test.go │ │ │ └── mocks │ │ │ └── event_producer.go │ ├── protocol │ │ └── protocol.go │ ├── transformer │ │ ├── cache │ │ │ ├── cache.go │ │ │ ├── cache_test.go │ │ │ └── mocks │ │ │ │ └── cache.go │ │ ├── constant.go │ │ ├── executor │ │ │ ├── mocks │ │ │ │ └── transformer.go │ │ │ ├── options.go │ │ │ ├── predictor.go │ │ │ ├── predictor_test.go │ │ │ ├── transformer.go │ │ │ └── transformer_test.go │ │ ├── feast │ │ │ ├── bigtablestore │ │ │ │ ├── client.go │ │ │ │ ├── client_test.go │ │ │ │ ├── encoding.go │ │ │ │ ├── encoding_test.go │ │ │ │ ├── feature.go │ │ │ │ ├── storage.go │ │ │ │ └── storage_test.go │ │ │ ├── call.go │ │ │ ├── call_test.go │ │ │ ├── client.go │ │ │ ├── compiler.go │ │ │ ├── default_value.go │ │ │ ├── enricher.go │ │ │ ├── enricher_test.go │ │ │ ├── entity_extractor.go │ │ │ ├── entity_extractor_test.go │ │ │ ├── feature_cache.go │ │ │ ├── feature_cache_test.go │ │ │ ├── feature_retriever.go │ │ │ ├── feature_retriever_bench_test.go │ │ │ ├── feature_retriever_test.go │ │ │ ├── feature_table.go │ │ │ ├── feature_table_test.go │ │ │ ├── instrumentation.go │ │ │ ├── mocks │ │ │ │ ├── core_service_client.go │ │ │ │ ├── feast_client.go │ │ │ │ └── feature_retriever.go │ │ │ ├── redis │ │ │ │ ├── client.go │ │ │ │ ├── client_test.go │ │ │ │ ├── encoding.go │ │ │ │ ├── encoding_test.go │ │ │ │ └── instrumentation.go │ │ │ ├── sdk.go │ │ │ ├── storage_config.go │ │ │ ├── storage_config_test.go │ │ │ ├── types.go │ │ │ ├── utils.go │ │ │ ├── utils_test.go │ │ │ ├── validator.go │ │ │ └── validator_test.go │ │ ├── jsonpath │ │ │ ├── jsonpath.go │ │ │ ├── jsonpath_test.go │ │ │ └── storage.go │ │ ├── pipeline │ │ │ ├── compiled_pipeline.go │ │ │ ├── compiler.go │ │ │ ├── compiler_test.go │ │ │ ├── create_table_op.go │ │ │ ├── create_table_op_test.go │ │ │ ├── encoder_op.go │ │ │ ├── encoder_op_test.go │ │ │ ├── environment.go │ │ │ ├── feast_op.go │ │ │ ├── feast_op_test.go │ │ │ ├── handler.go │ │ │ ├── json_output_op.go │ │ │ ├── json_output_op_test.go │ │ │ ├── mocks │ │ │ │ └── prediction_log_producer.go │ │ │ ├── operation.go │ │ │ ├── operation_test.go │ │ │ ├── options.go │ │ │ ├── prediction_log.go │ │ │ ├── prediction_log_op.go │ │ │ ├── prediction_log_op_test.go │ │ │ ├── table_join_op.go │ │ │ ├── table_join_op_test.go │ │ │ ├── table_transform_op.go │ │ │ ├── table_transform_op_test.go │ │ │ ├── testdata │ │ │ │ ├── input_output.yaml │ │ │ │ ├── invalid_encode_column.yaml │ │ │ │ ├── invalid_min_max_scale_column.yaml │ │ │ │ ├── invalid_output.yaml │ │ │ │ ├── invalid_preprocess_output.yaml │ │ │ │ ├── invalid_standard_scale_column.yaml │ │ │ │ ├── invalid_using_autoload.yaml │ │ │ │ ├── invalid_variables_in_transformations.yaml │ │ │ │ ├── postprocess_output_only.yaml │ │ │ │ ├── upi │ │ │ │ │ ├── invalid_postprocess_empty_result_table.yaml │ │ │ │ │ ├── invalid_postprocess_output.yaml │ │ │ │ │ ├── invalid_preprocess.yaml │ │ │ │ │ ├── invalid_preprocess_output.yaml │ │ │ │ │ ├── invalid_preprocess_output_empty.yaml │ │ │ │ │ ├── invalid_transformation_with_prediction_log.yaml │ │ │ │ │ ├── invalid_using_json_output.yaml │ │ │ │ │ ├── simple_preprocess_postprocess.yaml │ │ │ │ │ ├── table_transformer_with_prediction_log.yaml │ │ │ │ │ ├── valid_feast_preprocess.yaml │ │ │ │ │ ├── valid_passthrough.yaml │ │ │ │ │ ├── valid_table_transformer_preprocess.yaml │ │ │ │ │ └── valid_transformation_with_prediction_log.yaml │ │ │ │ ├── valid_encoder.yaml │ │ │ │ ├── valid_feast_expression.yaml │ │ │ │ ├── valid_feast_preprocess.yaml │ │ │ │ ├── valid_feast_series_transform.yaml │ │ │ │ ├── valid_feast_series_transform_conditional.yaml │ │ │ │ ├── valid_input_only.yaml │ │ │ │ ├── valid_no_pipeline.yaml │ │ │ │ ├── valid_passthrough.yaml │ │ │ │ ├── valid_preprocess_input_and_transformation.yaml │ │ │ │ ├── valid_preprocess_input_only.yaml │ │ │ │ ├── valid_preprocess_postprocess_transformation.yaml │ │ │ │ ├── valid_sequential_table_dependency.yaml │ │ │ │ ├── valid_simple_postprocess.yaml │ │ │ │ ├── valid_simple_preprocess.yaml │ │ │ │ ├── valid_simple_preprocess_child.yaml │ │ │ │ ├── valid_table_join.yaml │ │ │ │ ├── valid_table_join_multiple_columns.yaml │ │ │ │ ├── valid_table_transform_conditional_filtering.yaml │ │ │ │ └── valid_table_transform_preprocess.yaml │ │ │ ├── upi_autoloading_op.go │ │ │ ├── upi_autoloading_op_test.go │ │ │ ├── upi_postprocess_output_op.go │ │ │ ├── upi_postprocess_output_op_test.go │ │ │ ├── upi_preprocess_output_op.go │ │ │ ├── upi_preprocess_output_op_test.go │ │ │ ├── util.go │ │ │ ├── validator.go │ │ │ ├── validator_test.go │ │ │ ├── variable_op.go │ │ │ └── variable_op_test.go │ │ ├── server │ │ │ ├── config │ │ │ │ └── options.go │ │ │ ├── grpc │ │ │ │ ├── healthcheck.go │ │ │ │ ├── interceptors │ │ │ │ │ └── recoverer.go │ │ │ │ ├── mocks │ │ │ │ │ └── universal_prediction_service_client.go │ │ │ │ ├── predictor.go │ │ │ │ ├── server.go │ │ │ │ └── server_test.go │ │ │ ├── instrumentation │ │ │ │ └── instrumentation.go │ │ │ ├── response │ │ │ │ └── error.go │ │ │ └── rest │ │ │ │ ├── middleware │ │ │ │ └── recoverer.go │ │ │ │ ├── server.go │ │ │ │ └── server_test.go │ │ ├── spec │ │ │ ├── common.pb.go │ │ │ ├── common.pb.json.go │ │ │ ├── encoder.pb.go │ │ │ ├── encoder.pb.json.go │ │ │ ├── env_custom_decoder.go │ │ │ ├── feast.pb.go │ │ │ ├── feast.pb.json.go │ │ │ ├── json.pb.go │ │ │ ├── json.pb.json.go │ │ │ ├── prediction_log.pb.go │ │ │ ├── prediction_log.pb.json.go │ │ │ ├── scaler.pb.go │ │ │ ├── scaler.pb.json.go │ │ │ ├── standard_transformer.pb.go │ │ │ ├── standard_transformer.pb.json.go │ │ │ ├── storage.pb.go │ │ │ ├── storage.pb.json.go │ │ │ ├── table.pb.go │ │ │ ├── table.pb.json.go │ │ │ ├── upi_autoload.pb.go │ │ │ ├── upi_autoload.pb.json.go │ │ │ ├── upi_output.pb.go │ │ │ ├── upi_output.pb.json.go │ │ │ ├── variable.pb.go │ │ │ └── variable.pb.json.go │ │ ├── symbol │ │ │ ├── function │ │ │ │ ├── geospatial.go │ │ │ │ ├── time.go │ │ │ │ └── time_test.go │ │ │ ├── geospatial.go │ │ │ ├── geospatial_test.go │ │ │ ├── json.go │ │ │ ├── json_test.go │ │ │ ├── operator.go │ │ │ ├── operator_test.go │ │ │ ├── registry.go │ │ │ ├── registry_test.go │ │ │ ├── statistics.go │ │ │ ├── statistics_test.go │ │ │ ├── time.go │ │ │ ├── time_bench_test.go │ │ │ └── time_test.go │ │ └── types │ │ │ ├── converter │ │ │ ├── converter.go │ │ │ └── converter_test.go │ │ │ ├── encoder │ │ │ ├── cyclical_encoder.go │ │ │ ├── cyclical_encoder_test.go │ │ │ ├── ordinal_encoder.go │ │ │ └── ordinal_encoder_test.go │ │ │ ├── expression │ │ │ └── storage.go │ │ │ ├── feast │ │ │ └── feast.go │ │ │ ├── feature_table.go │ │ │ ├── feature_table_test.go │ │ │ ├── json.go │ │ │ ├── operation │ │ │ ├── arithmetic.go │ │ │ ├── comparator.go │ │ │ ├── comparator_test.go │ │ │ ├── logical.go │ │ │ ├── operation.go │ │ │ ├── operation_test.go │ │ │ └── operator.go │ │ │ ├── operation_tracing.go │ │ │ ├── payload.go │ │ │ ├── pipeline.go │ │ │ ├── prediction_result.go │ │ │ ├── scaler │ │ │ ├── min_max_scaler.go │ │ │ ├── min_max_scaler_test.go │ │ │ ├── scaler.go │ │ │ ├── standard_scaler.go │ │ │ └── standard_scaler_test.go │ │ │ ├── series │ │ │ ├── series.go │ │ │ └── series_test.go │ │ │ └── table │ │ │ ├── table.go │ │ │ ├── table_bench_test.go │ │ │ ├── table_converter.go │ │ │ ├── table_converter_test.go │ │ │ ├── table_test.go │ │ │ └── testdata │ │ │ ├── blank.csv │ │ │ ├── header_only.csv │ │ │ ├── header_only.parquet │ │ │ ├── normal.csv │ │ │ └── normal.parquet │ └── validator │ │ └── validator.go ├── queue │ ├── dispatcher.go │ ├── dispatcher_test.go │ ├── errors.go │ ├── job.go │ ├── mocks │ │ └── producer.go │ ├── work │ │ ├── batch_deployment.go │ │ ├── batch_deployment_test.go │ │ ├── model_service_deployment.go │ │ ├── model_service_deployment_test.go │ │ ├── observability_publisher_deployment.go │ │ └── observability_publisher_deployment_test.go │ └── worker.go ├── service │ ├── deployment_service.go │ ├── deployment_service_test.go │ ├── environment_service.go │ ├── environment_service_test.go │ ├── log_service.go │ ├── log_service_test.go │ ├── mocks │ │ ├── deployment_service.go │ │ ├── endpoints_service.go │ │ ├── environment_service.go │ │ ├── list_options.go │ │ ├── log_service.go │ │ ├── model_endpoint_alert_service.go │ │ ├── model_endpoints_service.go │ │ ├── model_schema_service.go │ │ ├── models_service.go │ │ ├── prediction_job_service.go │ │ ├── projects_service.go │ │ ├── secret_service.go │ │ ├── transformer_service.go │ │ ├── version_image_service.go │ │ └── versions_service.go │ ├── model_endpoint_alert_service.go │ ├── model_endpoint_alert_service_test.go │ ├── model_endpoint_service.go │ ├── model_endpoint_service_test.go │ ├── model_schema_service.go │ ├── model_schema_service_test.go │ ├── model_service.go │ ├── options.go │ ├── pagination.go │ ├── prediction_job_service.go │ ├── prediction_job_service_test.go │ ├── project_service.go │ ├── project_service_test.go │ ├── secret_service.go │ ├── secret_service_test.go │ ├── testdata │ │ └── model_endpoint_alert.yaml │ ├── transformer_simulation_service.go │ ├── transformer_simulation_service_test.go │ ├── utils_test.go │ ├── version_endpoint_service.go │ ├── version_endpoint_service_test.go │ ├── version_image_service.go │ ├── version_image_service_test.go │ ├── version_service.go │ ├── version_service_it_test.go │ └── version_service_test.go ├── storage │ ├── alert_storage.go │ ├── alert_storage_test.go │ ├── deployment_storage.go │ ├── deployment_storage_test.go │ ├── mocks │ │ ├── alert_storage.go │ │ ├── deployment_storage.go │ │ ├── model_endpoint_storage.go │ │ ├── model_schema_storage.go │ │ ├── observability_publisher_storage.go │ │ ├── prediction_job_storage.go │ │ ├── version_endpoint_storage.go │ │ └── version_storage.go │ ├── model_endpoint_storage.go │ ├── model_endpoint_storage_test.go │ ├── model_schema_storage.go │ ├── model_schema_storage_test.go │ ├── observability_publisher_storage.go │ ├── observability_publisher_storage_test.go │ ├── prediction_job_storage.go │ ├── prediction_job_storage_test.go │ ├── version_endpoint_storage.go │ ├── version_endpoint_storage_test.go │ ├── version_storage.go │ └── version_storage_test.go ├── tests │ └── matcher │ │ └── matcher.go ├── utils │ ├── crypto.go │ ├── crypto_test.go │ ├── kubernetes.go │ ├── kubernetes_test.go │ ├── labels.go │ ├── labels_test.go │ ├── maps.go │ ├── maps_test.go │ ├── model_location.go │ ├── stacktrace.go │ ├── strings.go │ ├── table.go │ ├── table_test.go │ ├── urls.go │ └── urls_test.go ├── warden │ ├── mocks │ │ └── warden.go │ ├── warden.go │ └── warden_test.go └── webhook │ ├── mocks │ └── webhook.go │ ├── request.go │ ├── webhook.go │ └── webhook_test.go ├── config.yaml ├── db-migrations ├── 10_prediction_job.down.sql ├── 10_prediction_job.up.sql ├── 11_model_endpoint_alerts.down.sql ├── 11_model_endpoint_alerts.up.sql ├── 12_deployment.down.sql ├── 12_deployment.up.sql ├── 13_max_vars.down.sql ├── 13_max_vars.up.sql ├── 14_prediction_job_environment.down.sql ├── 14_prediction_job_environment.up.sql ├── 15_project_label.down.sql ├── 15_project_label.up.sql ├── 16_prediction_job_metadata.down.sql ├── 16_prediction_job_metadata.up.sql ├── 17_move_project_secret_out.down.sql ├── 17_move_project_secret_out.up.sql ├── 18_prediction_jobs_index.down.sql ├── 18_prediction_jobs_index.up.sql ├── 19_transformer.down.sql ├── 19_transformer.up.sql ├── 1_schema_creation.down.sql ├── 1_schema_creation.up.sql ├── 21_logger_column_on_version_endpoints.down.sql ├── 21_logger_column_on_version_endpoints.up.sql ├── 22_standard_transformer.down.sql ├── 22_standard_transformer.up.sql ├── 23_version_labels.down.sql ├── 23_version_labels.up.sql ├── 24_job.down.sql ├── 24_job.up.sql ├── 25_message_varchar_length.down.sql ├── 25_message_varchar_length.up.sql ├── 26_custom_predictor_on_versions.down.sql ├── 26_custom_predictor_on_versions.up.sql ├── 27_deployment_mode.down.sql ├── 27_deployment_mode.up.sql ├── 28_autoscaling_policy.down.sql ├── 28_autoscaling_policy.up.sql ├── 29_protocol.down.sql ├── 29_protocol.up.sql ├── 2_multi_env.down.sql ├── 2_multi_env.up.sql ├── 30_versions_python_version.down.sql ├── 30_versions_python_version.up.sql ├── 31_environments_gpus.down.sql ├── 31_environments_gpus.up.sql ├── 32_revision_id.down.sql ├── 32_revision_id.up.sql ├── 33_update_multiple_deployments.down.sql ├── 33_update_multiple_deployments.up.sql ├── 34_version_endpoints_enable_observability.down.sql ├── 34_version_endpoints_enable_observability.up.sql ├── 35_image_builder.down.sql ├── 35_image_builder.up.sql ├── 36_model_schemas.down.sql ├── 36_model_schemas.up.sql ├── 37_environments_add_max_allowed_replicas.down.sql ├── 37_environments_add_max_allowed_replicas.up.sql ├── 38_supported_observability_on_models.down.sql ├── 38_supported_observability_on_models.up.sql ├── 39_observability_publisher.down.sql ├── 39_observability_publisher.up.sql ├── 3_deployment_status.down.sql ├── 3_deployment_status.up.sql ├── 40_model_observability_on_version_endpoints.down.sql ├── 40_model_observability_on_version_endpoints.up.sql ├── 41_version_endpoints_add_secrets.down.sql ├── 41_version_endpoints_add_secrets.up.sql ├── 4_env_add_columns.down.sql ├── 4_env_add_columns.up.sql ├── 5_delete_triggers.down.sql ├── 5_delete_triggers.up.sql ├── 6_resource_request.down.sql ├── 6_resource_request.up.sql ├── 7_env_var.down.sql ├── 7_env_var.up.sql ├── 8_authz.down.sql ├── 8_authz.up.sql ├── 9_secret.down.sql └── 9_secret.up.sql ├── docker-compose.yaml ├── docs ├── .gitignore ├── Makefile ├── README.md ├── SUMMARY.md ├── developer │ ├── architecture.md │ ├── e2e-test.md │ └── local-development.md ├── diagrams │ ├── architecture.drawio.svg │ ├── e2e-architecture.drawio.svg │ ├── model_deployment_serving.drawio.svg │ ├── model_observability.drawio.svg │ ├── prediction_job_lifecycle.drawio.svg │ └── user_flow.drawio.svg ├── images │ ├── autoscaling_policy.png │ ├── batch_resource_configuration.png │ ├── batch_resource_metrics.png │ ├── configure_alert.png │ ├── configure_alert_models_list.png │ ├── configure_standard_transformer.gif │ ├── delete_model_active_entity.png │ ├── delete_model_no_entity.png │ ├── delete_model_version_active_entity.png │ ├── delete_model_version_inactive_entity.png │ ├── delete_model_version_no_entity.png │ ├── deploy_model_version.png │ ├── deployment_errors │ │ ├── crashloopbackoff.png │ │ ├── image_building_oomkilled.png │ │ ├── image_not_found.png │ │ └── version_log_history_tab.png │ ├── deployment_mode.png │ ├── merlin-image-only.png │ ├── merlin-with-text.png │ ├── override_cpu_limits.png │ ├── redeploy_model_unsuccessful.png │ ├── redeploy_model_version.png │ ├── serve_model_version.png │ ├── standard_transformer.png │ ├── upi_autoloading_config.png │ └── upi_preprocess_output.png ├── maintainer │ ├── generated │ │ ├── 00_setting_up.md │ │ └── 01_troubleshooting.md │ ├── templates │ │ ├── 00_setting_up.md │ │ └── 01_troubleshooting.md │ └── values.json └── user │ ├── generated │ ├── 00_introduction.md │ ├── 01_getting_started.md │ ├── 02_creating_a_model.md │ ├── 03_deploying_a_model.md │ ├── 04_deleting_a_model.md │ ├── 05_configuring_alerts.md │ ├── 06_batch_prediction.md │ ├── 07_examples.md │ ├── 08_limitations.md │ ├── 09_troubleshooting_deployment_errors.md │ ├── 10_model_schema.md │ ├── 11_model_observability.md │ ├── 12_build_image.md │ ├── examples │ │ ├── 01_standard_model.md │ │ ├── 02_pyfunc_model.md │ │ ├── 03_transformer.md │ │ ├── 04_batch_prediction.md │ │ └── 05_others.md │ ├── model_deployment │ │ ├── 01_deploying_a_model_version.md │ │ ├── 02_serving_a_model_version.md │ │ ├── 03_configuring_transformers.md │ │ ├── 04_redeploying_a_model_version.md │ │ └── transformer │ │ │ ├── 01_standard_transformer.md │ │ │ ├── 02_custom_transformer.md │ │ │ └── standard_transformer │ │ │ ├── 01_standard_transformer_expressions.md │ │ │ └── 02_standard_transformer_upi.md │ └── model_types │ │ └── 01_custom_model.md │ ├── templates │ ├── 00_introduction.md │ ├── 01_getting_started.md │ ├── 02_creating_a_model.md │ ├── 03_deploying_a_model.md │ ├── 04_deleting_a_model.md │ ├── 05_configuring_alerts.md │ ├── 06_batch_prediction.md │ ├── 07_examples.md │ ├── 08_limitations.md │ ├── 09_troubleshooting_deployment_errors.md │ ├── 10_model_schema.md │ ├── 11_model_observability.md │ ├── 12_build_image.md │ ├── examples │ │ ├── 01_standard_model.md │ │ ├── 02_pyfunc_model.md │ │ ├── 03_transformer.md │ │ ├── 04_batch_prediction.md │ │ └── 05_others.md │ ├── model_deployment │ │ ├── 01_deploying_a_model_version.md │ │ ├── 02_serving_a_model_version.md │ │ ├── 03_configuring_transformers.md │ │ ├── 04_redeploying_a_model_version.md │ │ └── transformer │ │ │ ├── 01_standard_transformer.md │ │ │ ├── 02_custom_transformer.md │ │ │ └── standard_transformer │ │ │ ├── 01_standard_transformer_expressions.md │ │ │ └── 02_standard_transformer_upi.md │ ├── model_types │ │ └── 01_custom_model.md │ └── webhook │ │ └── 01_webhook_config.md │ └── values.json ├── environment.yaml ├── examples ├── batch │ ├── BatchPredictionTutorial1-IrisClassifier.ipynb │ ├── BatchPredictionTutorial2-NewYorkTaxi .ipynb │ ├── env.yaml │ ├── model │ │ ├── .keep │ │ ├── model.joblib │ │ └── nyc-model.joblib │ └── requirements.txt ├── custom-model │ ├── fastapi │ │ ├── custom-model.ipynb │ │ ├── requirements.txt │ │ └── server │ │ │ ├── app.py │ │ │ ├── boot.sh │ │ │ └── dockerfile │ ├── http_json │ │ ├── Dockerfile │ │ ├── custom-model.ipynb │ │ ├── model-server │ │ │ ├── README.md │ │ │ ├── go.mod │ │ │ ├── go.sum │ │ │ ├── main.go │ │ │ ├── model │ │ │ │ ├── payload.go │ │ │ │ └── xgboost.go │ │ │ └── response.go │ │ └── requirements.txt │ └── upi_v1 │ │ ├── Dockerfile │ │ ├── cmd │ │ └── main.go │ │ ├── custom-model-upi.ipynb │ │ ├── go.mod │ │ ├── go.sum │ │ ├── pkg │ │ ├── interceptors │ │ │ └── recoverer.go │ │ └── server │ │ │ ├── config.go │ │ │ ├── instrumentation.go │ │ │ └── server.go │ │ └── requirements.txt ├── metrics │ ├── Metrics.ipynb │ ├── env.yaml │ └── requirements.txt ├── model-endpoint │ ├── ModelEndpoint.ipynb │ ├── requirements.txt │ └── sklearn-model │ │ ├── .keep │ │ └── model.joblib ├── pyfunc-upi │ ├── Pyfunc.ipynb │ ├── env.yaml │ ├── requirements.txt │ └── xgboost-model │ │ ├── .keep │ │ └── model.json ├── pyfunc │ ├── Pyfunc.ipynb │ ├── env.yaml │ ├── requirements.txt │ ├── sklearn-model │ │ ├── .keep │ │ └── model_2.joblib │ └── xgboost-model │ │ ├── .keep │ │ └── model_1.bst ├── pytorch │ ├── Pytorch.ipynb │ ├── pytorch-model │ │ ├── config │ │ │ └── config.properties │ │ ├── iris_handler.py │ │ └── model.py │ └── requirements.txt ├── resource-request-gpu │ ├── Resource-Request-GPU.ipynb │ ├── requirements.txt │ └── sklearn-model │ │ ├── .keep │ │ └── model.joblib ├── resource-request │ ├── Resource-Request.ipynb │ ├── requirements.txt │ └── sklearn-model │ │ ├── .keep │ │ └── model.joblib ├── sklearn │ ├── SKLearn.ipynb │ ├── requirements.txt │ └── sklearn-model │ │ └── .keep ├── tensorflow │ ├── Tensorflow.ipynb │ └── requirements.txt ├── transformer │ ├── .gitignore │ ├── custom-transformer │ │ ├── PyFunc-Transformer.ipynb │ │ ├── PyTorch-Transformer.ipynb │ │ ├── env.yaml │ │ ├── input-raw-image.json │ │ ├── input-tensor.json │ │ ├── pytorch-model │ │ │ ├── model.pt │ │ │ └── model.py │ │ └── requirements.txt │ ├── feast-enricher-transformer │ │ ├── Feast-Enricher.ipynb │ │ ├── env.yaml │ │ ├── feast_enricher_config.yaml │ │ └── requirements.txt │ ├── standard-transformer │ │ ├── Standard-Transformer.ipynb │ │ ├── config.yaml │ │ ├── env.yaml │ │ ├── request.json │ │ └── requirements.txt │ └── upi │ │ ├── Standard Transformer.ipynb │ │ ├── config.yaml │ │ ├── env.yaml │ │ └── requirements.txt └── xgboost │ ├── XGBoost.ipynb │ ├── requirements.txt │ └── xgboost-model │ ├── .keep │ └── model.bst ├── inference-logger.Dockerfile ├── infra └── kafka │ ├── Makefile │ ├── docker-compose.yaml │ └── update_run.sh ├── mlflow ├── Dockerfile └── README.md ├── openapi-api-codegen.yaml ├── openapi-sdk-codegen.yaml ├── protos └── merlin │ ├── log │ └── inference_log.proto │ └── transformer │ └── spec │ ├── common.proto │ ├── encoder.proto │ ├── feast.proto │ ├── json.proto │ ├── prediction_log.proto │ ├── scaler.proto │ ├── standard_transformer.proto │ ├── storage.proto │ ├── table.proto │ ├── upi_autoload.proto │ ├── upi_output.proto │ └── variable.proto ├── python ├── .dockerignore ├── Makefile ├── batch-predictor │ ├── .gitignore │ ├── Makefile │ ├── Pipfile │ ├── README.md │ ├── docker │ │ ├── app.Dockerfile │ │ ├── base.Dockerfile │ │ ├── local-app.Dockerfile │ │ ├── main.py │ │ ├── merlin_entrypoint.sh │ │ └── process_conda_env.sh │ ├── go.mod │ ├── go.sum │ ├── integration_test │ │ ├── merlin │ │ │ ├── .helmignore │ │ │ ├── Chart.yaml │ │ │ ├── templates │ │ │ │ ├── _helpers.tpl │ │ │ │ ├── jobspec-configmap.yaml │ │ │ │ ├── serviceaccount-secret.yaml │ │ │ │ └── spark-application.yaml │ │ │ └── values.yaml │ │ └── scripts │ │ │ ├── init-credentials.sh │ │ │ ├── run-test.sh │ │ │ └── tear-down.sh │ ├── merlinpyspark │ │ ├── __init__.py │ │ ├── __main__.py │ │ ├── config.py │ │ ├── model.py │ │ ├── sink.py │ │ ├── source.py │ │ ├── spec │ │ │ ├── __init__.py │ │ │ ├── prediction_job_pb2.py │ │ │ └── prediction_job_pb2.pyi │ │ └── version.py │ ├── pkg │ │ ├── github.com │ │ │ └── caraml-dev │ │ │ │ └── merlin-pyspark-app │ │ │ │ └── pkg │ │ │ │ └── spec │ │ │ │ └── prediction_job.pb.json.go │ │ └── spec │ │ │ ├── prediction_job.pb.go │ │ │ └── prediction_job.pb.json.go │ ├── requirements.txt │ ├── requirements_test.txt │ ├── sample │ │ └── sample_1.yaml │ ├── setup.py │ ├── spec │ │ └── prediction_job.proto │ ├── test-config │ │ └── .keep │ ├── test-model │ │ ├── MLmodel │ │ ├── artifacts │ │ │ └── model.joblib │ │ ├── conda.yaml │ │ ├── python_env.yaml │ │ ├── python_model.pkl │ │ └── requirements.txt │ └── test │ │ ├── __init__.py │ │ ├── config_test.py │ │ ├── conftest.py │ │ ├── main_test.py │ │ ├── model_test.py │ │ ├── sink_test.py │ │ ├── source_test.py │ │ └── util │ │ └── test_utils.py ├── observation-publisher │ ├── .gitignore │ ├── Dockerfile │ ├── Makefile │ ├── README.md │ ├── conf │ │ ├── config.yaml │ │ └── environment │ │ │ └── example-override.yaml │ ├── publisher │ │ ├── __init__.py │ │ ├── __main__.py │ │ ├── config.py │ │ ├── metric.py │ │ ├── observation_sink.py │ │ ├── prediction_log_consumer.py │ │ └── prediction_log_parser.py │ ├── pyproject.toml │ ├── requirements-dev.txt │ ├── requirements.in │ ├── requirements.txt │ └── tests │ │ ├── __init__.py │ │ ├── common_fixtures.py │ │ ├── test_config.py │ │ ├── test_observation_sink.py │ │ └── test_prediction_log_consumer.py ├── pyfunc-scaffolding │ ├── .gitignore │ ├── cookiecutter.json │ └── {{cookiecutter.model_slug}} │ │ ├── .env.sample │ │ ├── .gitignore │ │ ├── Pipfile │ │ ├── README.md │ │ ├── deployment.py │ │ ├── env │ │ └── conda.yaml │ │ ├── requirements.txt │ │ └── src │ │ ├── constant.py │ │ ├── util │ │ └── prometheus.py │ │ └── {{cookiecutter.model_slug}}.py ├── pyfunc-server │ ├── .gitignore │ ├── Makefile │ ├── Pipfile │ ├── README.md │ ├── benchmark │ │ ├── benchmark.sh │ │ ├── large.cfg │ │ ├── large.json │ │ ├── medium.cfg │ │ ├── medium.json │ │ ├── small.cfg │ │ └── small.json │ ├── docker │ │ ├── Dockerfile │ │ ├── base.Dockerfile │ │ ├── local.Dockerfile │ │ ├── process_conda_env.sh │ │ └── run.sh │ ├── examples │ │ ├── __init__.py │ │ ├── echo_http │ │ │ ├── __init__.py │ │ │ ├── echo_http.py │ │ │ └── env.yaml │ │ ├── echo_upi │ │ │ ├── README.md │ │ │ ├── __init__.py │ │ │ ├── env.yaml │ │ │ ├── upi_client.py │ │ │ └── upi_server.py │ │ └── iris_http │ │ │ ├── __init__.py │ │ │ ├── env.yaml │ │ │ ├── iris_http.py │ │ │ └── models │ │ │ ├── model_1.bst │ │ │ └── model_2.joblib │ ├── prometheus │ │ └── .keep │ ├── pyfuncserver │ │ ├── __init__.py │ │ ├── __main__.py │ │ ├── config.py │ │ ├── metrics │ │ │ ├── __init__.py │ │ │ ├── handler.py │ │ │ └── pusher.py │ │ ├── model │ │ │ ├── __init__.py │ │ │ └── model.py │ │ ├── protocol │ │ │ ├── __init__.py │ │ │ ├── rest │ │ │ │ ├── __init__.py │ │ │ │ ├── handler.py │ │ │ │ └── server.py │ │ │ └── upi │ │ │ │ ├── __init__.py │ │ │ │ └── server.py │ │ ├── publisher │ │ │ ├── __init__.py │ │ │ ├── kafka.py │ │ │ └── publisher.py │ │ ├── sampler │ │ │ ├── __init__.py │ │ │ └── sampler.py │ │ ├── server.py │ │ ├── utils │ │ │ ├── __init__.py │ │ │ ├── contants.py │ │ │ └── converter.py │ │ └── version.py │ ├── requirements.txt │ ├── setup.py │ └── test │ │ ├── __init__.py │ │ ├── local-artifacts │ │ ├── MLmodel │ │ ├── README.md │ │ ├── artifacts │ │ │ ├── model_1.bst │ │ │ └── model_2.joblib │ │ ├── conda.yaml │ │ ├── python_env.yaml │ │ ├── python_model.pkl │ │ └── requirements.txt │ │ ├── publisher │ │ ├── __init__.py │ │ ├── test_kafka.py │ │ └── test_publisher.py │ │ ├── sampler │ │ ├── __init__.py │ │ └── test_sampler.py │ │ ├── test_backward_compatibility.py │ │ ├── test_examples.py │ │ ├── test_http.py │ │ ├── test_upi.py │ │ ├── util │ │ ├── __init__.py │ │ └── test_converter.py │ │ └── utils.py └── sdk │ ├── .dockerignore │ ├── .gitignore │ ├── .openapi-generator-ignore │ ├── Dockerfile │ ├── Makefile │ ├── Pipfile │ ├── README.md │ ├── client │ ├── __init__.py │ ├── api │ │ ├── __init__.py │ │ ├── alert_api.py │ │ ├── endpoint_api.py │ │ ├── environment_api.py │ │ ├── log_api.py │ │ ├── model_endpoints_api.py │ │ ├── model_schema_api.py │ │ ├── models_api.py │ │ ├── prediction_jobs_api.py │ │ ├── project_api.py │ │ ├── secret_api.py │ │ ├── standard_transformer_api.py │ │ ├── version_api.py │ │ └── version_image_api.py │ ├── api_client.py │ ├── api_response.py │ ├── configuration.py │ ├── exceptions.py │ ├── models │ │ ├── __init__.py │ │ ├── alert_condition_metric_type.py │ │ ├── alert_condition_severity.py │ │ ├── autoscaling_policy.py │ │ ├── binary_classification_output.py │ │ ├── build_image_options.py │ │ ├── config.py │ │ ├── container.py │ │ ├── custom_predictor.py │ │ ├── deployment_mode.py │ │ ├── endpoint_status.py │ │ ├── env_var.py │ │ ├── environment.py │ │ ├── file_format.py │ │ ├── gpu_config.py │ │ ├── gpu_toleration.py │ │ ├── ground_truth_job.py │ │ ├── ground_truth_source.py │ │ ├── image_building_job_state.py │ │ ├── image_building_job_status.py │ │ ├── label.py │ │ ├── list_jobs_paginated_response.py │ │ ├── logger.py │ │ ├── logger_config.py │ │ ├── logger_mode.py │ │ ├── metrics_type.py │ │ ├── mock_response.py │ │ ├── model.py │ │ ├── model_endpoint.py │ │ ├── model_endpoint_alert.py │ │ ├── model_endpoint_alert_condition.py │ │ ├── model_endpoint_rule.py │ │ ├── model_endpoint_rule_destination.py │ │ ├── model_observability.py │ │ ├── model_prediction_config.py │ │ ├── model_prediction_output.py │ │ ├── model_prediction_output_class.py │ │ ├── model_schema.py │ │ ├── mounted_mlp_secret.py │ │ ├── operation_tracing.py │ │ ├── paging.py │ │ ├── pipeline_tracing.py │ │ ├── prediction_job.py │ │ ├── prediction_job_config.py │ │ ├── prediction_job_config_bigquery_sink.py │ │ ├── prediction_job_config_bigquery_source.py │ │ ├── prediction_job_config_gcs_sink.py │ │ ├── prediction_job_config_gcs_source.py │ │ ├── prediction_job_config_maxcompute_sink.py │ │ ├── prediction_job_config_maxcompute_source.py │ │ ├── prediction_job_config_model.py │ │ ├── prediction_job_config_model_result.py │ │ ├── prediction_job_resource_request.py │ │ ├── prediction_log_ingestion_resource_request.py │ │ ├── prediction_logger_config.py │ │ ├── project.py │ │ ├── protocol.py │ │ ├── ranking_output.py │ │ ├── regression_output.py │ │ ├── resource_request.py │ │ ├── result_type.py │ │ ├── save_mode.py │ │ ├── schema_spec.py │ │ ├── secret.py │ │ ├── standard_transformer_simulation_request.py │ │ ├── standard_transformer_simulation_response.py │ │ ├── transformer.py │ │ ├── value_type.py │ │ ├── version.py │ │ ├── version_endpoint.py │ │ └── version_image.py │ └── rest.py │ ├── client_README.md │ ├── docs │ ├── Dockerfile │ ├── Makefile │ ├── README.md │ ├── conf.py │ ├── getting_started.rst │ ├── index.rst │ ├── introduction.rst │ ├── requirements_docs.txt │ ├── sample │ │ ├── batch_iris.nblink │ │ ├── batch_taxi.nblink │ │ ├── metrics.nblink │ │ ├── notebooks.rst │ │ ├── pyfunc.nblink │ │ ├── pytorch.nblink │ │ ├── sample_metric.png │ │ ├── sklearn.nblink │ │ ├── tensorflow.nblink │ │ └── xgboost.nblink │ ├── serve.json │ ├── source │ │ ├── merlin.batch.rst │ │ ├── merlin.docker.rst │ │ ├── merlin.rst │ │ └── modules.rst │ └── sphinxcontrib │ │ ├── __init__.py │ │ └── confluencebuilder_nbsphinx │ │ └── __init__.py │ ├── merlin │ ├── __init__.py │ ├── autoscaling.py │ ├── batch │ │ ├── __init__.py │ │ ├── big_query_util.py │ │ ├── config.py │ │ ├── job.py │ │ ├── maxcompute_util.py │ │ ├── sink.py │ │ └── source.py │ ├── client.py │ ├── deployment_mode.py │ ├── docker │ │ ├── __init__.py │ │ ├── docker.py │ │ ├── pyfunc.Dockerfile │ │ └── standard.Dockerfile │ ├── endpoint.py │ ├── environment.py │ ├── fluent.py │ ├── logger.py │ ├── merlin.py │ ├── model.py │ ├── model_observability.py │ ├── model_schema.py │ ├── mounted_mlp_secret.py │ ├── observability │ │ ├── __init__.py │ │ └── inference.py │ ├── protocol.py │ ├── pyfunc.py │ ├── requirements.py │ ├── resource_request.py │ ├── transformer.py │ ├── util.py │ ├── validation.py │ ├── version.py │ └── version_image.py │ ├── merlin_cli.gif │ ├── pytest.ini │ ├── requirements.txt │ ├── requirements_test.txt │ ├── setup.py │ └── test │ ├── __init__.py │ ├── batch │ ├── big_query_util_test.py │ ├── model │ │ └── env.yaml │ ├── sink_unit_test.py │ └── source_unit_test.py │ ├── batch_integration_test.py │ ├── build_image_integration_test.py │ ├── cli_integration_test.py │ ├── client_test.py │ ├── conftest.py │ ├── custom-model │ ├── input.json │ └── model.bst │ ├── docker │ └── docker_test.py │ ├── feast_model.py │ ├── integration_test.py │ ├── invalid-models │ ├── onnx-model-invalid │ │ └── invalid.onnx │ ├── pyfunc-model-invalid │ │ ├── MLmodel │ │ ├── conda.yaml │ │ └── invalid_model.pkl │ ├── pytorch-model-invalid │ │ └── invalid.pt │ ├── sklearn-model-invalid │ │ └── invalid.joblib │ ├── tensorflow-model-invalid │ │ └── 1 │ │ │ ├── invalid_model.pb │ │ │ └── variables │ │ │ ├── variables.data-00000-of-00002 │ │ │ ├── variables.data-00001-of-00002 │ │ │ └── variables.index │ └── xgboost-model-invalid │ │ └── invalid.bst │ ├── local_server_test.py │ ├── logger_test.py │ ├── merlin_test.py │ ├── model_observability_test.py │ ├── model_schema_test.py │ ├── model_test.py │ ├── model_validation_test.py │ ├── observability_test.py │ ├── onnx-model │ └── model.onnx │ ├── pyfunc │ ├── env.yaml │ ├── model_1.bst │ ├── model_2.joblib │ └── table_data │ │ └── normal.csv │ ├── pyfunc_integration_test.py │ ├── pyfunc_test.py │ ├── pyfunc_upi_integration_test.py │ ├── pytorch-model │ ├── iris.pt │ ├── iris.py │ ├── iris_handler.py │ └── pytorch-sample │ │ ├── config │ │ └── config.properties │ │ └── model-store │ │ └── pytorch-sample.mar │ ├── requirements │ ├── empty_in.yaml │ ├── empty_out.yaml │ ├── no_pip_reqs_in.yaml │ ├── no_pip_reqs_out.yaml │ ├── non_package_reqs_in.yaml │ ├── non_package_reqs_out.yaml │ ├── other_reqs_in.yaml │ ├── other_reqs_out.yaml │ ├── pyfunc_server_with_version_in.yaml │ ├── pyfunc_server_with_version_out.yaml │ ├── pyfunc_server_without_version_in.yaml │ ├── pyfunc_server_without_version_out.yaml │ └── with-requirements-txt │ │ ├── constraints_in.yaml │ │ ├── constraints_out.yaml │ │ ├── empty.txt │ │ ├── empty_in.yaml │ │ ├── empty_out.yaml │ │ ├── no_pyfunc_server.txt │ │ ├── no_pyfunc_server_in.yaml │ │ ├── no_pyfunc_server_out.yaml │ │ ├── pyfunc_server_with_version.txt │ │ ├── pyfunc_server_with_version_in.yaml │ │ ├── pyfunc_server_with_version_out.yaml │ │ ├── pyfunc_server_without_version.txt │ │ ├── pyfunc_server_without_version_in.yaml │ │ └── pyfunc_server_without_version_out.yaml │ ├── requirements_test.py │ ├── resource_request_test.py │ ├── sklearn-model │ ├── .keep │ └── model.joblib │ ├── tensorflow-model │ └── 1 │ │ ├── saved_model.pb │ │ └── variables │ │ ├── variables.data-00000-of-00002 │ │ ├── variables.data-00001-of-00002 │ │ └── variables.index │ ├── transformer │ ├── .gitignore │ ├── feast_enricher.yaml │ ├── input.json │ ├── model.pt │ ├── requirements.txt │ ├── sim_exp_resp_feast_w_tracing.json │ ├── sim_exp_resp_feast_wo_tracing.json │ ├── sim_exp_resp_valid_w_tracing.json │ ├── sim_exp_resp_valid_wo_tracing.json │ ├── standard_transformer_feast_with_source.yaml.tmpl │ ├── standard_transformer_multiple_feast.yaml.tmpl │ ├── standard_transformer_no_feast.yaml │ ├── standard_transformer_with_feast.yaml │ └── upi_standard_transformer_no_feast.yaml │ ├── transformer_test.py │ ├── utils.py │ ├── utils_unit_test.py │ └── xgboost-model │ ├── .keep │ └── model.bst ├── scripts ├── e2e │ ├── Makefile │ ├── README.md │ ├── config │ │ ├── coredns │ │ │ └── patch.yaml │ │ ├── istio │ │ │ ├── clusterlocal-gateway.yaml │ │ │ ├── ingress-class.yaml │ │ │ ├── ingress-gateway.yaml │ │ │ └── istiod.yaml │ │ ├── knative │ │ │ ├── kustomization.yaml │ │ │ └── overlay.yaml │ │ ├── kserve │ │ │ ├── kustomization.yaml │ │ │ └── overlay.yaml │ │ ├── minio │ │ │ └── values.yaml │ │ └── mock │ │ │ └── message-dumper.yaml │ ├── debug-e2e.sh │ ├── deploy-merlin.sh │ ├── docs │ │ └── E2E_arch.png │ ├── run-e2e.sh │ ├── setup-and-run-e2e.sh │ ├── setup-cluster.sh │ └── values-e2e.yaml ├── quick_install.sh └── vertagen │ └── vertagen.sh ├── swagger.yaml ├── transformer.Dockerfile └── ui ├── .dockerignore ├── .env.development ├── .gitignore ├── README.md ├── package.json ├── public ├── android-chrome-192x192.png ├── android-chrome-512x512.png ├── apple-touch-icon.png ├── browserconfig.xml ├── favicon-16x16.png ├── favicon-32x32.png ├── favicon.ico ├── index.html ├── logo.svg ├── mstile-144x144.png ├── mstile-150x150.png ├── mstile-310x150.png ├── mstile-310x310.png ├── mstile-70x70.png ├── robots.txt ├── safari-pinned-tab.svg └── site.webmanifest ├── src ├── App.js ├── AppRoutes.js ├── Home.js ├── PrivateLayout.js ├── PrivateLayout.scss ├── assets │ ├── icon │ │ ├── gcp-bigtable.svg │ │ └── rocket.svg │ └── scss │ │ ├── EnvironmentVariables.scss │ │ ├── Secrets.scss │ │ ├── index.scss │ │ └── style.scss ├── bootstrap.js ├── components │ ├── ContainerConfigTable.js │ ├── CopyableUrl.js │ ├── CursorPagination.js │ ├── DeploymentStatus.js │ ├── EnvVarsConfigTable.js │ ├── HorizontalDescriptionList.js │ ├── PageTitle.js │ ├── ResourcesConfigTable.js │ ├── SecretsConfigTable.js │ ├── TabNavigation.js │ ├── logs │ │ ├── ContainerLogsView.js │ │ └── LogsSearchBar.js │ ├── modals │ │ ├── DeleteModelModal.js │ │ ├── DeleteModelPyFuncV2Modal.js │ │ ├── DeleteModelVersionModal.js │ │ ├── DeleteModelVersionPyFuncV2Modal.js │ │ ├── ServeVersionEndpointModal.js │ │ ├── StopServeVersionEndpointModal.js │ │ ├── UndeployVersionEndpointModal.js │ │ └── index.js │ ├── section.js │ └── section.scss ├── config.js ├── hooks │ ├── useFeastApi.js │ └── useMerlinApi.js ├── index.js ├── mocks │ ├── data │ │ ├── container.json │ │ ├── containerList.json │ │ ├── environmentList.json │ │ ├── job.json │ │ ├── jobList.json │ │ ├── model.json │ │ ├── modelEndpointAlert.json │ │ ├── modelList.json │ │ ├── noBody.json │ │ ├── project.json │ │ ├── projectList.json │ │ ├── transformerSimulationOutput.json │ │ ├── versionEndpoint.json │ │ ├── versionEndpointList.json │ │ └── versionList.json │ └── index.js ├── model │ ├── ModelDetails.js │ ├── ModelEndpointActions.js │ ├── ModelListTable.js │ ├── Models.js │ └── alert │ │ ├── ModelAlert.js │ │ └── components │ │ ├── ModelAlertForm.js │ │ ├── ModelAlertRule.js │ │ ├── ModelAlertRulePercentile.js │ │ └── ModelAlertRulePercentileRow.js ├── pages │ ├── index.js │ ├── job │ │ ├── CreateJobPage.js │ │ ├── JobPage.js │ │ ├── ListJobsPage.js │ │ ├── RecreateJobPage.js │ │ ├── components │ │ │ ├── JobActions.js │ │ │ ├── JobConfig.js │ │ │ ├── JobDetailTabNavigation.js │ │ │ ├── JobErrorMessage.js │ │ │ ├── JobInfoPanel.js │ │ │ ├── JobLog.js │ │ │ ├── JobRunPanel.js │ │ │ ├── JobSearchBar.js │ │ │ ├── JobStatus.js │ │ │ ├── JobsTable.js │ │ │ ├── OtherConfig.js │ │ │ ├── ResourcesConfigTable.js │ │ │ ├── SinkConfig.js │ │ │ └── SourceConfig.js │ │ ├── form │ │ │ ├── JobForm.js │ │ │ ├── JobFormOthers.js │ │ │ ├── JobFormSink.js │ │ │ ├── JobFormSource.js │ │ │ ├── JobFormStep.js │ │ │ ├── components │ │ │ │ ├── ClusteredFieldsComboBox.js │ │ │ │ ├── EnvironmentVariablesForm.js │ │ │ │ ├── FeaturesComboBox.js │ │ │ │ ├── ModelVersionsSelect.js │ │ │ │ ├── ResourceRequestForm.js │ │ │ │ ├── ResultDataTypeSelect.js │ │ │ │ ├── SecretsForm.js │ │ │ │ └── ServiceAccountSelect.js │ │ │ └── context.js │ │ ├── modals │ │ │ └── StopPredictionJobModal.js │ │ └── utils │ │ │ ├── bigquery.js │ │ │ ├── breadcrumbs.js │ │ │ └── monitoringUrl.js │ ├── tools │ │ ├── StandardTransformerTools.js │ │ └── TransformerTools.js │ └── version │ │ ├── DeploymentActions.js │ │ ├── DeploymentConfigPanel.js │ │ ├── DeploymentPanelHeader.js │ │ ├── EndpointDetails.js │ │ ├── EnvironmentDropdown.js │ │ ├── HistoryDetails.js │ │ ├── ModelServicePanel.js │ │ ├── ModelVersionPanelHeader.js │ │ ├── TransformerServicePanel.js │ │ ├── VersionDetails.js │ │ ├── VersionTabNavigation.js │ │ ├── components │ │ ├── forms │ │ │ ├── DeployModelVersionForm.js │ │ │ ├── components │ │ │ │ ├── AutoscalingPolicyFormGroup.js │ │ │ │ ├── CPULimitsFormGroup.js │ │ │ │ ├── CostEstimationPanel.js │ │ │ │ ├── DeploymentConfigPanel.js │ │ │ │ ├── DeploymentModeDropdown.js │ │ │ │ ├── DeploymentSummary.js │ │ │ │ ├── DraggableHeader.js │ │ │ │ ├── EnvVariablesPanel.js │ │ │ │ ├── EnvironmentDropdownOption.js │ │ │ │ ├── ImageBuilderSection.js │ │ │ │ ├── LoggerPanel.js │ │ │ │ ├── Panel.js │ │ │ │ ├── ProtocolDropdown.js │ │ │ │ ├── ResourcesPanel.js │ │ │ │ ├── SecretsPanel.js │ │ │ │ ├── SecretsPanel.scss │ │ │ │ ├── SelectTransformerPanel.js │ │ │ │ ├── docker_config │ │ │ │ │ └── DockerDeploymentPanel.js │ │ │ │ ├── feast_config │ │ │ │ │ ├── FeastEnricherPanel.js │ │ │ │ │ └── components │ │ │ │ │ │ ├── FeastEntities.js │ │ │ │ │ │ ├── FeastFeatures.js │ │ │ │ │ │ ├── FeastInputCard.js │ │ │ │ │ │ ├── FeastInputCard.scss │ │ │ │ │ │ ├── FeastInputGroup.js │ │ │ │ │ │ ├── FeastProjectComboBox.js │ │ │ │ │ │ └── FeastServingUrlSelect.js │ │ │ │ └── transformer │ │ │ │ │ ├── InputPanel.js │ │ │ │ │ ├── JsonPathConfigInput.js │ │ │ │ │ ├── OutputPanel.js │ │ │ │ │ ├── PipelineSidebarPanel.js │ │ │ │ │ ├── PipelineStage.js │ │ │ │ │ ├── RowCell.scss │ │ │ │ │ ├── TransformationPanel.js │ │ │ │ │ ├── TransformerSimulation.js │ │ │ │ │ └── components │ │ │ │ │ ├── AddButton.js │ │ │ │ │ ├── PredictionLoggerPanel.js │ │ │ │ │ ├── TransformationGraph.js │ │ │ │ │ ├── TransformationGraph.scss │ │ │ │ │ ├── TransformationSpec.js │ │ │ │ │ ├── simulation │ │ │ │ │ ├── PipelineNode.js │ │ │ │ │ ├── PipelineNodeDetails.js │ │ │ │ │ ├── TransformerSimulationInput.js │ │ │ │ │ ├── TransformerSimulationOutput.js │ │ │ │ │ ├── TransformerSimulationOutputTracing.js │ │ │ │ │ └── constants.js │ │ │ │ │ ├── table_inputs │ │ │ │ │ ├── AutoloadCard.js │ │ │ │ │ ├── CyclicalEncoderInputGroup.js │ │ │ │ │ ├── EncoderInputCard.js │ │ │ │ │ ├── EncodersInputGroup.js │ │ │ │ │ ├── OrdinalEncoderInputGroup.js │ │ │ │ │ ├── OrdinalEncoderMapper.js │ │ │ │ │ ├── SelectCyclicalEncodeType.js │ │ │ │ │ ├── SelectEncoder.js │ │ │ │ │ ├── SelectPeriodType.js │ │ │ │ │ ├── SelectValueType.js │ │ │ │ │ ├── TableColumnsInput.js │ │ │ │ │ ├── TableFromFileSchema.js │ │ │ │ │ ├── TableInputCard.js │ │ │ │ │ ├── VariablesInput.js │ │ │ │ │ └── VariablesInputCard.js │ │ │ │ │ ├── table_operations │ │ │ │ │ ├── ColumnsComboBox.js │ │ │ │ │ ├── ConditionalUpdateCard.js │ │ │ │ │ ├── ConditionalUpdatePanel.js │ │ │ │ │ ├── EncodeColumns.js │ │ │ │ │ ├── FilterRow.js │ │ │ │ │ ├── RenameColumns.js │ │ │ │ │ ├── ScaleColumnCard.js │ │ │ │ │ ├── ScaleColumnsGroup.js │ │ │ │ │ ├── SelectScaler.js │ │ │ │ │ ├── SelectTableJoin.js │ │ │ │ │ ├── SelectTableOperation.js │ │ │ │ │ ├── SelectUpdateColumnStrategy.js │ │ │ │ │ ├── SliceRow.js │ │ │ │ │ ├── SortColumns.js │ │ │ │ │ ├── TableJoinCard.js │ │ │ │ │ ├── TableTransformationCard.js │ │ │ │ │ ├── TableTransformationStepCard.js │ │ │ │ │ ├── TableTransformationStepPanel.js │ │ │ │ │ ├── UpdateColumnCard.js │ │ │ │ │ ├── UpdateColumnPanel.js │ │ │ │ │ └── UpdateColumns.js │ │ │ │ │ └── table_outputs │ │ │ │ │ ├── BaseJsonOutputCard.js │ │ │ │ │ ├── JsonOutputCard.js │ │ │ │ │ ├── JsonOutputFieldCard.js │ │ │ │ │ ├── SelectJsonFormat.js │ │ │ │ │ ├── UpiPostprocessOutputCard.js │ │ │ │ │ └── UpiPreprocessOutputCard.js │ │ │ ├── steps │ │ │ │ ├── CustomTransformerStep.js │ │ │ │ ├── FeastTransformerStep.js │ │ │ │ ├── ModelStep.js │ │ │ │ ├── Pipeline.scss │ │ │ │ ├── PipelineStage.js │ │ │ │ ├── PredictionLoggerStep.js │ │ │ │ ├── StandardTransformerStep.js │ │ │ │ └── TransformerStep.js │ │ │ └── validation │ │ │ │ └── schema.js │ │ └── modal │ │ │ ├── ModelVersionEndpointsTable.js │ │ │ └── ModelVersionJobsTable.js │ │ ├── deploy │ │ └── DeployModelVersionView.js │ │ └── redeploy │ │ └── RedeployModelVersionView.js ├── providers │ ├── docker │ │ └── context.js │ ├── environments │ │ └── context.js │ └── feast │ │ ├── FeastProjectsContext.js │ │ └── FeastResourcesContext.js ├── serviceWorker.js ├── services │ ├── job │ │ └── Job.js │ ├── logger │ │ ├── Logger.js │ │ └── Logger.test.js │ ├── transformer │ │ ├── Transformer.js │ │ ├── TransformerConfig.js │ │ ├── TransformerConfig.test.js │ │ └── testdata │ │ │ └── standard_transformer.yaml │ ├── version │ │ └── Version.js │ └── version_endpoint │ │ └── VersionEndpoint.js ├── utils │ ├── costEstimation.js │ ├── createStackdriverUrl.js │ ├── kubernetesResourceParser.js │ └── versionEndpointUrl.js ├── validation │ └── validateBigquery.js └── version │ ├── VersionEndpointActions.js │ ├── VersionListTable.js │ └── Versions.js └── yarn.lock /.dockerignore: -------------------------------------------------------------------------------- 1 | .git/ 2 | .github/ 3 | .gitignore 4 | .dockerignore 5 | 6 | bin/ 7 | build/ 8 | docs/ 9 | examples/ 10 | python/pyfunc-scaffolding 11 | python/pyfunc-server 12 | python/sdk 13 | scripts/ 14 | 15 | Dockerfile 16 | 17 | README.md 18 | -------------------------------------------------------------------------------- /.gitbook.yaml: -------------------------------------------------------------------------------- 1 | root: ./docs/ 2 | 3 | structure: 4 | readme: README.md -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | --- 8 | 9 | ## Expected Behavior 10 | 11 | ## Current Behavior 12 | 13 | ## Steps to reproduce 14 | 15 | ### Specifications 16 | 17 | - Version: 18 | - Platform: 19 | - Subsystem: 20 | 21 | ## Possible Solution 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | --- 8 | 9 | ## Is your feature request related to a problem? Please describe. 10 | 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | ## Describe the solution you'd like 14 | 15 | A clear and concise description of what you want to happen. 16 | 17 | ## Describe alternatives you've considered 18 | 19 | A clear and concise description of any alternative solutions or features you've considered. 20 | 21 | ## Additional context 22 | 23 | Add any other context or screenshots about the feature request here. 24 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | changelog: 2 | exclude: 3 | labels: 4 | - ignore-for-release 5 | categories: 6 | - title: Breaking Changes 🛠 7 | labels: 8 | - breaking 9 | - title: New Features 🎉 10 | labels: 11 | - enhancement 12 | - title: Bug Fixes 🐞 13 | - bug 14 | - title: Documentation 📄 15 | - documentation 16 | - title: Other Changes 17 | labels: 18 | - "*" 19 | -------------------------------------------------------------------------------- /.github/workflows/pr-checks.yaml: -------------------------------------------------------------------------------- 1 | name: Pull Request Labels 2 | on: 3 | pull_request: 4 | types: [opened, labeled, unlabeled, synchronize] 5 | jobs: 6 | label: 7 | runs-on: ubuntu-latest 8 | permissions: 9 | issues: write 10 | pull-requests: write 11 | steps: 12 | - uses: mheap/github-action-required-labels@v5 13 | with: 14 | mode: minimum 15 | count: 1 16 | labels: "breaking, bug, enhancement, documentation, maintenance" # Permissible PR labels 17 | -------------------------------------------------------------------------------- /.github/workflows/trigger-caraml-doc-sync.yml: -------------------------------------------------------------------------------- 1 | # This workflow triggers sync of docs to caraml-dev/docs 2 | 3 | name: Trigger CaraML Docs Sync 4 | 5 | on: 6 | push: 7 | tags: 8 | - v* 9 | 10 | jobs: 11 | trigger-sync: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Trigger Remote Doc Sync Workflow 15 | uses: caraml-dev/docs/.github/actions/trigger-remote-docs-sync@main 16 | with: 17 | module: 'model' 18 | git_https: 'https://github.com/caraml-dev/merlin.git' 19 | doc_folder: 'docs' 20 | ref_name: ${{ github.ref_name }} 21 | ref_type: ${{ github.ref_type }} 22 | credentials: ${{ secrets.CARAML_SYNC }} -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | # Required 6 | version: 1 7 | 8 | # Build documentation in the docs/ directory with Sphinx 9 | sphinx: 10 | configuration: python/sdk/docs/conf.py 11 | 12 | # Optionally set the version of Python and requirements required to build your docs 13 | python: 14 | version: 3.9 15 | install: 16 | - requirements: python/sdk/docs/requirements_docs.txt -------------------------------------------------------------------------------- /OWNERS: -------------------------------------------------------------------------------- 1 | approvers: 2 | - ariefrahmansyah 3 | - pradithya 4 | - tiopramayudi 5 | reviewers: 6 | - ariefrahmansyah 7 | - pradithya 8 | - tiopramayudi 9 | -------------------------------------------------------------------------------- /api/.golangci.yml: -------------------------------------------------------------------------------- 1 | run: 2 | timeout: 10m 3 | modules-download-mode: readonly 4 | 5 | linters: 6 | # See https://golangci-lint.run/usage/linters/ 7 | enable: 8 | - errorlint 9 | - goimports 10 | - gofmt 11 | - bodyclose 12 | 13 | issues: 14 | exclude-use-default: false 15 | max-issues-per-linter: 0 16 | max-same-issues: 0 17 | exclude-dirs: 18 | - client 19 | - '.*/mocks/.*' 20 | -------------------------------------------------------------------------------- /api/api/deployment_api.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | ) 7 | 8 | type DeploymentController struct { 9 | *AppContext 10 | } 11 | 12 | func (c *DeploymentController) ListDeployments(r *http.Request, vars map[string]string, _ interface{}) *Response { 13 | deployments, err := c.DeploymentService.ListDeployments(vars["model_id"], vars["version_id"], vars["endpoint_id"]) 14 | if err != nil { 15 | return InternalServerError(fmt.Sprintf("Error listing deployments: %v", err)) 16 | } 17 | 18 | return Ok(deployments) 19 | } 20 | -------------------------------------------------------------------------------- /api/api/response_test.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/google/go-cmp/cmp" 7 | "github.com/google/go-cmp/cmp/cmpopts" 8 | ) 9 | 10 | // Utility function to compare responses without the stacktrace 11 | func assertEqualResponses(t *testing.T, want interface{}, got interface{}) { 12 | options := []cmp.Option{ 13 | cmp.AllowUnexported(Response{}), 14 | cmpopts.IgnoreFields(Response{}, "stacktrace"), 15 | } 16 | if !cmp.Equal(want, got, options...) { 17 | t.Errorf("Responses mismatched") 18 | t.Log(cmp.Diff(want, got, options...)) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /api/cmd/inference-logger/Makefile: -------------------------------------------------------------------------------- 1 | APP_NAME=inference-logger 2 | LOG_URL?=kafka:localhost:9092 3 | 4 | .PHONY: build 5 | build: 6 | @go build -o bin/${APP_NAME} ./main.go 7 | 8 | .PHONY: run 9 | run: 10 | @rm /tmp/agent.sock || true 11 | @SERVING_READINESS_PROBE='{"tcpSocket":{"port":8080,"host":"127.0.0.1"},"successThreshold":1}' UNIX_SOCKET_PATH="/tmp/agent.sock" ./bin/${APP_NAME} -log-url="${LOG_URL}" 12 | 13 | .PHONY: run-mock 14 | run-mock: 15 | @go run ../../pkg/inference-logger/mock-server/mock_server.go 16 | -------------------------------------------------------------------------------- /api/config/testdata/bigtable-config.yaml: -------------------------------------------------------------------------------- 1 | StandardTransformerConfig: 2 | FeastBigtableConfig: 3 | ServingURL: 10.1.1.3 4 | Project: gcp-project 5 | IsUsingDirectStorage: true 6 | Instance: instance 7 | AppProfile: default 8 | PoolSize: 3 9 | KeepAliveInterval: 2m 10 | KeepAliveTimeout: 1m 11 | -------------------------------------------------------------------------------- /api/config/testdata/config-1.yaml: -------------------------------------------------------------------------------- 1 | DbConfig: 2 | Host: 10.148.176.5 3 | Port: 5432 4 | Database: merlin 5 | User: merlin 6 | Password: WmU3blo4OTFzS3J1 7 | -------------------------------------------------------------------------------- /api/config/testdata/invalid-bigtable-config-no-instance.yaml: -------------------------------------------------------------------------------- 1 | StandardTransformerConfig: 2 | FeastBigtableConfig: 3 | ServingURL: 10.1.1.3 4 | Project: gcp-project 5 | IsUsingDirectStorage: true 6 | AppProfile: default 7 | PoolSize: 3 8 | KeepAliveInterval: 2m 9 | KeepAliveTimeout: 1m 10 | -------------------------------------------------------------------------------- /api/config/testdata/invalid-bigtable-config-no-pool-size.yaml: -------------------------------------------------------------------------------- 1 | StandardTransformerConfig: 2 | FeastBigtableConfig: 3 | ServingURL: 10.1.1.3 4 | Project: gcp-project 5 | IsUsingDirectStorage: true 6 | Instance: instance 7 | AppProfile: default 8 | KeepAliveInterval: 2m 9 | KeepAliveTimeout: 1m 10 | -------------------------------------------------------------------------------- /api/config/testdata/invalid-bigtable-config-no-project.yaml: -------------------------------------------------------------------------------- 1 | StandardTransformerConfig: 2 | FeastBigtableConfig: 3 | ServingURL: 10.1.1.3 4 | IsUsingDirectStorage: true 5 | Instance: instance 6 | AppProfile: default 7 | PoolSize: 3 8 | KeepAliveInterval: 2m 9 | KeepAliveTimeout: 1m 10 | -------------------------------------------------------------------------------- /api/config/testdata/invalid-bigtable-config-no-serving-url.yaml: -------------------------------------------------------------------------------- 1 | StandardTransformerConfig: 2 | FeastBigtableConfig: 3 | Project: gcp-project 4 | IsUsingDirectStorage: true 5 | Instance: instance 6 | AppProfile: default 7 | PoolSize: 3 8 | KeepAliveInterval: 2m 9 | KeepAliveTimeout: 1m 10 | -------------------------------------------------------------------------------- /api/config/testdata/invalid-duration-format.yaml: -------------------------------------------------------------------------------- 1 | DbConfig: 2 | ConnMaxIdleTime: 30rr 3 | -------------------------------------------------------------------------------- /api/config/testdata/invalid-file-format.yaml: -------------------------------------------------------------------------------- 1 | Environment: dev 2 | Port: 8080 3 | LoggerDestinationURL: kafka:logger.destination:6668 4 | MLObsLoggerDestinationURL: 5 | Sentry: mlobs:kafka.destination:6668 6 | DSN: "" 7 | Enabled: false 8 | Labels: 9 | foo: bar 10 | -------------------------------------------------------------------------------- /api/config/testdata/invalid-redis-config-no-pool-size.yaml: -------------------------------------------------------------------------------- 1 | StandardTransformerConfig: 2 | FeastRedisConfig: 3 | ServingURL: online-storage.merlin.dev 4 | RedisAddresses: 5 | - 10.1.1.10 6 | - 10.1.1.11 7 | PoolSize: 0 8 | MaxRetries: 1 9 | DialTimeout: 10s 10 | -------------------------------------------------------------------------------- /api/config/testdata/invalid-redis-config-no-redis-addresses.yaml: -------------------------------------------------------------------------------- 1 | StandardTransformerConfig: 2 | FeastRedisConfig: 3 | ServingURL: online-storage.merlin.dev 4 | PoolSize: 4 5 | MaxRetries: 1 6 | DialTimeout: 10s 7 | -------------------------------------------------------------------------------- /api/config/testdata/invalid-redis-config-no-serving-url.yaml: -------------------------------------------------------------------------------- 1 | StandardTransformerConfig: 2 | FeastRedisConfig: 3 | RedisAddresses: 4 | - 10.1.1.10 5 | - 10.1.1.11 6 | PoolSize: 4 7 | MaxRetries: 1 8 | DialTimeout: 10s 9 | -------------------------------------------------------------------------------- /api/config/testdata/invalid-type.yaml: -------------------------------------------------------------------------------- 1 | Port: 2 | Value: 9999 -------------------------------------------------------------------------------- /api/config/testdata/redis-config.yaml: -------------------------------------------------------------------------------- 1 | StandardTransformerConfig: 2 | FeastRedisConfig: 3 | IsRedisCluster: true 4 | ServingURL: online-storage.merlin.dev 5 | RedisAddresses: 6 | - 10.1.1.10 7 | - 10.1.1.11 8 | PoolSize: 4 9 | MaxRetries: 1 10 | DialTimeout: 10s 11 | -------------------------------------------------------------------------------- /api/config/testdata/valid-imagebuilder-nodeselectors.yaml: -------------------------------------------------------------------------------- 1 | ImageBuilderConfig: 2 | NodeSelectors: 3 | cloud.google.com/gke-nodepool: image-build-job-node-pool 4 | -------------------------------------------------------------------------------- /api/config/testdata/valid-single-redis-cluster-config.yaml: -------------------------------------------------------------------------------- 1 | StandardTransformerConfig: 2 | FeastRedisConfig: 3 | IsRedisCluster: true 4 | ServingURL: online-storage.merlin.dev 5 | RedisAddresses: 6 | - 10.1.1.10 7 | - 10.1.1.11 8 | PoolSize: 4 9 | MaxRetries: 1 10 | DialTimeout: 10s 11 | MinIdleConn: 2 12 | -------------------------------------------------------------------------------- /api/config/testdata/valid-single-redis-config.yaml: -------------------------------------------------------------------------------- 1 | StandardTransformerConfig: 2 | FeastRedisConfig: 3 | IsRedisCluster: false 4 | ServingURL: online-storage.merlin.dev 5 | RedisAddresses: 6 | - 10.1.1.10 7 | - 10.1.1.11 8 | PoolSize: 4 9 | MaxRetries: 1 10 | DialTimeout: 10s 11 | -------------------------------------------------------------------------------- /api/mlflow/error.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The Merlin Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package mlflow 16 | 17 | const ( 18 | ResourceAlreadyExists = "RESOURCE_ALREADY_EXISTS" 19 | ) 20 | -------------------------------------------------------------------------------- /api/pkg/cronjob/cronjob.go: -------------------------------------------------------------------------------- 1 | package cronjob 2 | 3 | import "github.com/robfig/cron" 4 | 5 | // CronJob wraps robfig.Cron. 6 | type CronJob struct { 7 | c *cron.Cron 8 | } 9 | 10 | // New returns an initialized CronJob. 11 | func New() (*CronJob, error) { 12 | return &CronJob{ 13 | c: cron.New(), 14 | }, nil 15 | } 16 | 17 | // AddFunc adds a func to the CronJob to be run on the given schedule. 18 | func (c *CronJob) AddFunc(spec string, cmd func()) error { 19 | return c.c.AddFunc(spec, cmd) 20 | } 21 | 22 | // Start the cron scheduler in its own go-routine. 23 | func (c *CronJob) Start() { 24 | c.c.Start() 25 | } 26 | 27 | // Stop stops the cron scheduler if it is running; otherwise it does nothing. 28 | func (c *CronJob) Stop() { 29 | c.c.Stop() 30 | } 31 | -------------------------------------------------------------------------------- /api/pkg/cronjob/cronjob_test.go: -------------------------------------------------------------------------------- 1 | package cronjob 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestNew(t *testing.T) { 10 | c, err := New() 11 | assert.Nil(t, err) 12 | assert.NotNil(t, c) 13 | } 14 | -------------------------------------------------------------------------------- /api/pkg/deployment/deployment_mode.go: -------------------------------------------------------------------------------- 1 | package deployment 2 | 3 | // Mode mode of the deployment 4 | type Mode string 5 | 6 | const ( 7 | // ServerlessDeploymentMode uses knative service as deployment method 8 | ServerlessDeploymentMode Mode = "serverless" 9 | // RawDeploymentMode uses k8s deployment as deployment method 10 | RawDeploymentMode Mode = "raw_deployment" 11 | // EmptyMode 12 | EmptyDeploymentMode Mode = "" 13 | ) 14 | -------------------------------------------------------------------------------- /api/pkg/hystrix/logger.go: -------------------------------------------------------------------------------- 1 | package hystrix 2 | 3 | import "go.uber.org/zap" 4 | 5 | // hystrxLogger implements https://github.com/afex/hystrix-go/blob/master/hystrix/logger.go 6 | type hystrixLogger struct { 7 | zapLogger *zap.SugaredLogger 8 | } 9 | 10 | // newHystrixLogger create new instance of hystrixLogger backed by zap sugared logger. 11 | func NewHystrixLogger(zapLogger *zap.Logger) *hystrixLogger { 12 | return &hystrixLogger{zapLogger: zapLogger.Sugar()} 13 | } 14 | 15 | // Printf will format and log the arguments as INFO log 16 | func (l *hystrixLogger) Printf(format string, items ...interface{}) { 17 | l.zapLogger.Infof(format, items) 18 | } 19 | -------------------------------------------------------------------------------- /api/pkg/hystrix/util.go: -------------------------------------------------------------------------------- 1 | package hystrix 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | const ( 8 | maxUint = ^uint(0) 9 | maxInt = int(maxUint >> 1) 10 | ) 11 | 12 | func DurationToInt(duration, unit time.Duration) int { 13 | durationAsNumber := duration / unit 14 | 15 | if int64(durationAsNumber) > int64(maxInt) { 16 | // Returning max possible value seems like best possible solution here 17 | // the alternative is to panic as there is no way of returning an error 18 | // without changing the NewClient API 19 | return maxInt 20 | } 21 | return int(durationAsNumber) 22 | } 23 | -------------------------------------------------------------------------------- /api/pkg/inference-logger/logger/kafka_sink_test.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestGetTopicName(t *testing.T) { 10 | kafkaSink := &KafkaSink{ 11 | logger: nil, 12 | producer: nil, 13 | projectName: "my-project", 14 | modelName: "my-model", 15 | modelVersion: "1", 16 | } 17 | assert.Equal(t, "merlin-my-project-my-model-inference-log", kafkaSink.topicName()) 18 | } 19 | -------------------------------------------------------------------------------- /api/pkg/inference-logger/logger/log_sink.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | type LogSink interface { 4 | Sink(rawLogEntries []*LogEntry) error 5 | } 6 | -------------------------------------------------------------------------------- /api/pkg/protocol/protocol.go: -------------------------------------------------------------------------------- 1 | package protocol 2 | 3 | type Protocol string 4 | 5 | const ( 6 | // HttpJson protocol to be used when deploying model as HTTP/JSON server 7 | HttpJson Protocol = "HTTP_JSON" 8 | // UpiV1 protocol to be used when deploying UPI-compatible model 9 | UpiV1 Protocol = "UPI_V1" 10 | ) 11 | -------------------------------------------------------------------------------- /api/pkg/transformer/jsonpath/storage.go: -------------------------------------------------------------------------------- 1 | package jsonpath 2 | 3 | import "sync" 4 | 5 | type Storage struct { 6 | syncMap sync.Map 7 | } 8 | 9 | func NewStorage() *Storage { 10 | return &Storage{} 11 | } 12 | 13 | func (c *Storage) Get(jsonPath string) *Compiled { 14 | o, ok := c.syncMap.Load(jsonPath) 15 | if !ok { 16 | return nil 17 | } 18 | 19 | return o.(*Compiled) 20 | } 21 | 22 | func (c *Storage) Set(jsonPath string, compiledJsonPath *Compiled) { 23 | c.syncMap.Store(jsonPath, compiledJsonPath) 24 | } 25 | 26 | func (c *Storage) AddAll(jsonPaths map[string]*Compiled) { 27 | for k, v := range jsonPaths { 28 | c.Set(k, v) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /api/pkg/transformer/pipeline/prediction_log.go: -------------------------------------------------------------------------------- 1 | package pipeline 2 | 3 | import "context" 4 | 5 | // PredictionLog 6 | type PredictionLogProducer interface { 7 | Produce(ctx context.Context, value interface{}) error 8 | } 9 | -------------------------------------------------------------------------------- /api/pkg/transformer/pipeline/testdata/postprocess_output_only.yaml: -------------------------------------------------------------------------------- 1 | transformerConfig: 2 | postprocess: 3 | outputs: 4 | - jsonOutput: 5 | jsonTemplate: 6 | fields: 7 | - fieldName: instances 8 | fromJson: 9 | jsonPath: $.model_response.instances 10 | -------------------------------------------------------------------------------- /api/pkg/transformer/pipeline/testdata/upi/invalid_postprocess_empty_result_table.yaml: -------------------------------------------------------------------------------- 1 | transformerConfig: 2 | preprocess: 3 | inputs: 4 | - variables: 5 | - name: country 6 | jsonPath: $.prediction_context[0].string_value 7 | postprocess: 8 | inputs: 9 | - autoload: 10 | tableNames: 11 | - prediction_result 12 | transformations: 13 | - tableTransformation: 14 | inputTable: prediction_result 15 | outputTable: output_table 16 | steps: 17 | - updateColumns: 18 | - column: country 19 | expression: country 20 | outputs: 21 | - upiPostprocessOutput: 22 | predictionResultTableName: # failed due to empty result table name 23 | -------------------------------------------------------------------------------- /api/pkg/transformer/pipeline/testdata/upi/invalid_preprocess.yaml: -------------------------------------------------------------------------------- 1 | transformerConfig: 2 | preprocess: 3 | inputs: 4 | - autoload: 5 | tableNames: 6 | - table1 7 | - table2 8 | - table3 9 | variableNames: 10 | - var1 11 | - var2 12 | transformations: 13 | - tableTransformation: 14 | inputTable: table2 15 | outputTable: output_table 16 | steps: 17 | - updateColumns: 18 | - column: col1 19 | expression: var3 # failed the compilation since `var3` hasn't been declared before 20 | outputs: 21 | - upiPreprocessOutput: 22 | predictionTableName: output_table -------------------------------------------------------------------------------- /api/pkg/transformer/pipeline/testdata/upi/invalid_preprocess_output_empty.yaml: -------------------------------------------------------------------------------- 1 | transformerConfig: 2 | preprocess: 3 | inputs: 4 | - autoload: 5 | tableNames: 6 | - table1 7 | - table2 8 | - table3 9 | variableNames: 10 | - var1 11 | - var2 12 | transformations: 13 | - tableTransformation: 14 | inputTable: table2 15 | outputTable: output_table 16 | steps: 17 | - updateColumns: 18 | - column: col1 19 | expression: var2 20 | outputs: 21 | - upiPreprocessOutput: # failed due to empty `predictionTableName` and `transformerInputTableNames` 22 | predictionTableName: 23 | transformerInputTableNames: -------------------------------------------------------------------------------- /api/pkg/transformer/pipeline/testdata/upi/invalid_using_json_output.yaml: -------------------------------------------------------------------------------- 1 | transformerConfig: 2 | preprocess: 3 | inputs: 4 | - autoload: 5 | tableNames: 6 | - request_table 7 | - variables: 8 | - name: country 9 | jsonPath: $.prediction_context[0].string_value 10 | outputs: 11 | - jsonOutput: # json output is not supported for upi_v1 protocol 12 | jsonTemplate: 13 | fields: 14 | - fieldName: instances 15 | fromTable: 16 | tableName: "request_table" 17 | format: "SPLIT" 18 | -------------------------------------------------------------------------------- /api/pkg/transformer/pipeline/testdata/upi/simple_preprocess_postprocess.yaml: -------------------------------------------------------------------------------- 1 | transformerConfig: 2 | preprocess: 3 | inputs: 4 | - variables: 5 | - name: country 6 | jsonPath: $.prediction_context[0].string_value 7 | postprocess: 8 | inputs: 9 | - autoload: 10 | tableNames: 11 | - prediction_result 12 | transformations: 13 | - tableTransformation: 14 | inputTable: prediction_result 15 | outputTable: output_table 16 | steps: 17 | - updateColumns: 18 | - column: country 19 | expression: country 20 | outputs: 21 | - upiPostprocessOutput: 22 | predictionResultTableName: output_table 23 | -------------------------------------------------------------------------------- /api/pkg/transformer/pipeline/testdata/upi/valid_passthrough.yaml: -------------------------------------------------------------------------------- 1 | transformerConfig: 2 | preprocess: 3 | inputs: 4 | - autoload: 5 | tableNames: 6 | - table1 7 | outputs: 8 | - upiPreprocessOutput: 9 | predictionTableName: table1 10 | postprocess: 11 | inputs: 12 | - autoload: 13 | tableNames: 14 | - model_prediction_table 15 | outputs: 16 | - upiPostprocessOutput: 17 | predictionResultTableName: model_prediction_table 18 | -------------------------------------------------------------------------------- /api/pkg/transformer/pipeline/testdata/valid_no_pipeline.yaml: -------------------------------------------------------------------------------- 1 | transformerConfig: 2 | -------------------------------------------------------------------------------- /api/pkg/transformer/pipeline/testdata/valid_passthrough.yaml: -------------------------------------------------------------------------------- 1 | transformerConfig: 2 | preprocess: 3 | outputs: 4 | - jsonOutput: 5 | jsonTemplate: 6 | baseJson: 7 | jsonPath: $.raw_request 8 | postprocess: 9 | outputs: 10 | - jsonOutput: 11 | jsonTemplate: 12 | baseJson: 13 | jsonPath: $.model_response 14 | -------------------------------------------------------------------------------- /api/pkg/transformer/pipeline/testdata/valid_simple_postprocess.yaml: -------------------------------------------------------------------------------- 1 | transformerConfig: 2 | postprocess: 3 | inputs: 4 | - tables: 5 | - name: entity_table 6 | baseTable: 7 | fromJson: 8 | jsonPath: $.model_response.entities[*] 9 | addRowNumber: false 10 | outputs: 11 | - jsonOutput: 12 | jsonTemplate: 13 | fields: 14 | - fieldName: instances 15 | fromTable: 16 | tableName: "entity_table" 17 | format: "SPLIT" 18 | -------------------------------------------------------------------------------- /api/pkg/transformer/server/grpc/interceptors/recoverer.go: -------------------------------------------------------------------------------- 1 | package interceptors 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "google.golang.org/grpc" 8 | ) 9 | 10 | // PanicRecoveryInterceptor interceptor to recover after facing panic 11 | func PanicRecoveryInterceptor() grpc.UnaryServerInterceptor { 12 | return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { 13 | defer func(ctx context.Context) { 14 | if r := recover(); r != nil { 15 | if e, ok := r.(error); ok { 16 | err = e 17 | } else { 18 | err = fmt.Errorf("panic: %s", r) 19 | } 20 | } 21 | }(ctx) 22 | 23 | resp, err = handler(ctx, req) 24 | return resp, err 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /api/pkg/transformer/server/rest/middleware/recoverer.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "runtime/debug" 7 | 8 | "github.com/caraml-dev/merlin/pkg/transformer/server/response" 9 | ) 10 | 11 | func RecoveryHandler(next http.Handler) http.Handler { 12 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 13 | defer func() { 14 | if err := recover(); err != nil { 15 | debug.PrintStack() 16 | response.NewError(http.StatusInternalServerError, fmt.Errorf("panic: %v", err)).Write(w) 17 | } 18 | }() 19 | next.ServeHTTP(w, r) 20 | }) 21 | } 22 | -------------------------------------------------------------------------------- /api/pkg/transformer/spec/env_custom_decoder.go: -------------------------------------------------------------------------------- 1 | package spec 2 | 3 | import "fmt" 4 | 5 | func (source *ServingSource) Decode(value string) error { 6 | val, ok := ServingSource_value[value] 7 | if !ok { 8 | return fmt.Errorf("invalid serving source value %s", value) 9 | } 10 | *source = ServingSource(val) 11 | return nil 12 | } 13 | -------------------------------------------------------------------------------- /api/pkg/transformer/spec/prediction_log.pb.json.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-json. DO NOT EDIT. 2 | // source: transformer/spec/prediction_log.proto 3 | 4 | package spec 5 | 6 | import ( 7 | "google.golang.org/protobuf/encoding/protojson" 8 | ) 9 | 10 | // MarshalJSON implements json.Marshaler 11 | func (msg *PredictionLogConfig) MarshalJSON() ([]byte, error) { 12 | return protojson.MarshalOptions{ 13 | UseEnumNumbers: false, 14 | EmitUnpopulated: false, 15 | UseProtoNames: false, 16 | }.Marshal(msg) 17 | } 18 | 19 | // UnmarshalJSON implements json.Unmarshaler 20 | func (msg *PredictionLogConfig) UnmarshalJSON(b []byte) error { 21 | return protojson.UnmarshalOptions{ 22 | DiscardUnknown: false, 23 | }.Unmarshal(b, msg) 24 | } 25 | -------------------------------------------------------------------------------- /api/pkg/transformer/spec/upi_autoload.pb.json.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-json. DO NOT EDIT. 2 | // source: transformer/spec/upi_autoload.proto 3 | 4 | package spec 5 | 6 | import ( 7 | "google.golang.org/protobuf/encoding/protojson" 8 | ) 9 | 10 | // MarshalJSON implements json.Marshaler 11 | func (msg *UPIAutoload) MarshalJSON() ([]byte, error) { 12 | return protojson.MarshalOptions{ 13 | UseEnumNumbers: false, 14 | EmitUnpopulated: false, 15 | UseProtoNames: false, 16 | }.Marshal(msg) 17 | } 18 | 19 | // UnmarshalJSON implements json.Unmarshaler 20 | func (msg *UPIAutoload) UnmarshalJSON(b []byte) error { 21 | return protojson.UnmarshalOptions{ 22 | DiscardUnknown: false, 23 | }.Unmarshal(b, msg) 24 | } 25 | -------------------------------------------------------------------------------- /api/pkg/transformer/types/expression/storage.go: -------------------------------------------------------------------------------- 1 | package expression 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/antonmedv/expr/vm" 7 | ) 8 | 9 | type Storage struct { 10 | syncMap sync.Map 11 | } 12 | 13 | func NewStorage() *Storage { 14 | return &Storage{} 15 | } 16 | 17 | func (c *Storage) Get(expression string) *vm.Program { 18 | o, ok := c.syncMap.Load(expression) 19 | if !ok { 20 | return nil 21 | } 22 | 23 | return o.(*vm.Program) 24 | } 25 | 26 | func (c *Storage) Set(expression string, compiledExpression *vm.Program) { 27 | c.syncMap.Store(expression, compiledExpression) 28 | } 29 | 30 | func (c *Storage) AddAll(expressionMap map[string]*vm.Program) { 31 | for k, v := range expressionMap { 32 | c.Set(k, v) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /api/pkg/transformer/types/json.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import "github.com/caraml-dev/merlin/pkg/transformer/spec" 4 | 5 | type JSONObject map[string]interface{} 6 | 7 | type PayloadObjectContainer map[spec.PayloadType]Payload 8 | -------------------------------------------------------------------------------- /api/pkg/transformer/types/pipeline.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | type Pipeline string 4 | 5 | const ( 6 | Preprocess Pipeline = "preprocess" 7 | Postprocess Pipeline = "postprocess" 8 | ) 9 | -------------------------------------------------------------------------------- /api/pkg/transformer/types/prediction_result.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // PredictionResult holds information about response of gRPC request including its metadata 4 | type PredictionResult struct { 5 | Response Payload 6 | Error error 7 | Metadata PredictionMetadata 8 | } 9 | 10 | // PredictionMetadata contains information about model and project that produce the prediction 11 | type PredictionMetadata struct { 12 | ModelName string 13 | ModelVersion string 14 | Project string 15 | } 16 | -------------------------------------------------------------------------------- /api/pkg/transformer/types/table/testdata/blank.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/api/pkg/transformer/types/table/testdata/blank.csv -------------------------------------------------------------------------------- /api/pkg/transformer/types/table/testdata/header_only.csv: -------------------------------------------------------------------------------- 1 | First Name,Last Name,Age,Weight,Is VIP -------------------------------------------------------------------------------- /api/pkg/transformer/types/table/testdata/header_only.parquet: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/api/pkg/transformer/types/table/testdata/header_only.parquet -------------------------------------------------------------------------------- /api/pkg/transformer/types/table/testdata/normal.csv: -------------------------------------------------------------------------------- 1 | First Name,Last Name,Age,Weight,Is VIP 2 | Apple,Cider,25,48.8,TRUE 3 | Banana,Man,18,68,FALSE 4 | Zara,Vuitton,35,75,TRUE 5 | Sandra,Zawaska,32,55,FALSE 6 | Merlion,Krabby,23,57.22,FALSE -------------------------------------------------------------------------------- /api/pkg/transformer/types/table/testdata/normal.parquet: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/api/pkg/transformer/types/table/testdata/normal.parquet -------------------------------------------------------------------------------- /api/queue/errors.go: -------------------------------------------------------------------------------- 1 | package queue 2 | 3 | import "fmt" 4 | 5 | type RetryableError struct { 6 | Message string 7 | } 8 | 9 | func (err RetryableError) Error() string { 10 | return fmt.Sprintf("got retryable error %v", err.Message) 11 | } 12 | -------------------------------------------------------------------------------- /api/queue/job.go: -------------------------------------------------------------------------------- 1 | package queue 2 | 3 | import ( 4 | "database/sql/driver" 5 | "encoding/json" 6 | "errors" 7 | "time" 8 | ) 9 | 10 | type Arguments map[string]interface{} 11 | 12 | type Job struct { 13 | ID int64 `json:"id" gorm:"primary_key;"` 14 | Arguments Arguments `json:"arguments"` 15 | Completed bool `json:"completed"` 16 | Name string `json:"name"` 17 | CreatedAt time.Time `json:"created_at"` 18 | UpdatedAt time.Time `json:"updated_at"` 19 | } 20 | 21 | func (a Arguments) Value() (driver.Value, error) { 22 | return json.Marshal(a) 23 | } 24 | 25 | func (a *Arguments) Scan(value interface{}) error { 26 | b, ok := value.([]byte) 27 | if !ok { 28 | return errors.New("type assertion to []byte failed") 29 | } 30 | 31 | return json.Unmarshal(b, &a) 32 | } 33 | -------------------------------------------------------------------------------- /api/queue/mocks/producer.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v2.6.0. DO NOT EDIT. 2 | 3 | package mocks 4 | 5 | import ( 6 | queue "github.com/caraml-dev/merlin/queue" 7 | mock "github.com/stretchr/testify/mock" 8 | ) 9 | 10 | // Producer is an autogenerated mock type for the Producer type 11 | type Producer struct { 12 | mock.Mock 13 | } 14 | 15 | // EnqueueJob provides a mock function with given fields: job 16 | func (_m *Producer) EnqueueJob(job *queue.Job) error { 17 | ret := _m.Called(job) 18 | 19 | var r0 error 20 | if rf, ok := ret.Get(0).(func(*queue.Job) error); ok { 21 | r0 = rf(job) 22 | } else { 23 | r0 = ret.Error(0) 24 | } 25 | 26 | return r0 27 | } 28 | -------------------------------------------------------------------------------- /api/service/deployment_service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/caraml-dev/merlin/models" 5 | "github.com/caraml-dev/merlin/storage" 6 | ) 7 | 8 | type DeploymentService interface { 9 | ListDeployments(modelID, versionID, endpointUUID string) ([]*models.Deployment, error) 10 | } 11 | 12 | func NewDeploymentService(storage storage.DeploymentStorage) DeploymentService { 13 | return &deploymentService{ 14 | storage: storage, 15 | } 16 | } 17 | 18 | type deploymentService struct { 19 | storage storage.DeploymentStorage 20 | } 21 | 22 | func (service *deploymentService) ListDeployments(modelID, versionID, endpointUUID string) ([]*models.Deployment, error) { 23 | // TODO: Add pagination 24 | return service.storage.ListInModelVersion(modelID, versionID, endpointUUID) 25 | } 26 | -------------------------------------------------------------------------------- /api/utils/labels.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "regexp" 6 | ) 7 | 8 | var validLabelRegex = regexp.MustCompile("^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$") 9 | 10 | func IsValidLabel(name string) error { 11 | lengthOfName := len(name) < 64 12 | if !(lengthOfName) { 13 | return fmt.Errorf("length of name is greater than 63 characters") 14 | } 15 | 16 | if isValidName := validLabelRegex.MatchString(name); !isValidName { 17 | return fmt.Errorf("name violates kubernetes label constraint") 18 | } 19 | 20 | return nil 21 | } 22 | -------------------------------------------------------------------------------- /api/utils/table.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "github.com/jedib0t/go-pretty/v6/table" 5 | ) 6 | 7 | func LogTable(headers []string, rows [][]string) string { 8 | t := table.NewWriter() 9 | t.SetStyle(table.StyleLight) 10 | 11 | headerRow := table.Row{} 12 | for _, v := range headers { 13 | headerRow = append(headerRow, v) 14 | } 15 | t.AppendHeader(headerRow) 16 | 17 | for _, v := range rows { 18 | tableRow := table.Row{} 19 | for _, cell := range v { 20 | tableRow = append(tableRow, cell) 21 | } 22 | t.AppendRow(tableRow) 23 | } 24 | 25 | return t.Render() 26 | } 27 | -------------------------------------------------------------------------------- /api/warden/mocks/warden.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.0.0. DO NOT EDIT. 2 | 3 | package mocks 4 | 5 | import mock "github.com/stretchr/testify/mock" 6 | 7 | // Client is an autogenerated mock type for the Client type 8 | type Client struct { 9 | mock.Mock 10 | } 11 | 12 | // GetAllTeams provides a mock function with given fields: 13 | func (_m *Client) GetAllTeams() ([]string, error) { 14 | ret := _m.Called() 15 | 16 | var r0 []string 17 | if rf, ok := ret.Get(0).(func() []string); ok { 18 | r0 = rf() 19 | } else { 20 | if ret.Get(0) != nil { 21 | r0 = ret.Get(0).([]string) 22 | } 23 | } 24 | 25 | var r1 error 26 | if rf, ok := ret.Get(1).(func() error); ok { 27 | r1 = rf() 28 | } else { 29 | r1 = ret.Error(1) 30 | } 31 | 32 | return r0, r1 33 | } 34 | -------------------------------------------------------------------------------- /api/webhook/request.go: -------------------------------------------------------------------------------- 1 | package webhook 2 | 3 | import ( 4 | "github.com/caraml-dev/mlp/api/pkg/webhooks" 5 | ) 6 | 7 | type WebhookRequest struct { 8 | EventType webhooks.EventType `json:"event_type"` 9 | Data map[string]interface{} `json:"data"` 10 | } 11 | -------------------------------------------------------------------------------- /db-migrations/11_model_endpoint_alerts.down.sql: -------------------------------------------------------------------------------- 1 | -- Copyright 2020 The Merlin Authors 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | DROP TABLE model_endpoint_alerts; 16 | DROP INDEX model_endpoint_alerts_idx_1; 17 | -------------------------------------------------------------------------------- /db-migrations/12_deployment.down.sql: -------------------------------------------------------------------------------- 1 | -- Copyright 2020 The Merlin Authors 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | DROP TABLE IF EXISTS deployments CASCADE; -------------------------------------------------------------------------------- /db-migrations/13_max_vars.down.sql: -------------------------------------------------------------------------------- 1 | -- Copyright 2020 The Merlin Authors 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | ALTER TABLE environments DROP COLUMN max_cpu; 16 | ALTER TABLE environments DROP COLUMN max_memory; 17 | -------------------------------------------------------------------------------- /db-migrations/13_max_vars.up.sql: -------------------------------------------------------------------------------- 1 | -- Copyright 2020 The Merlin Authors 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | ALTER TABLE environments ADD COLUMN max_cpu varchar; 16 | ALTER TABLE environments ADD COLUMN max_memory varchar; 17 | -------------------------------------------------------------------------------- /db-migrations/14_prediction_job_environment.down.sql: -------------------------------------------------------------------------------- 1 | -- Copyright 2020 The Merlin Authors 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | ALTER TABLE prediction_jobs DROP COLUMN environment_name; -------------------------------------------------------------------------------- /db-migrations/15_project_label.down.sql: -------------------------------------------------------------------------------- 1 | -- Copyright 2020 The Merlin Authors 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | ALTER TABLE projects DROP COLUMN team, 16 | DROP COLUMN stream, 17 | DROP COLUMN labels; -------------------------------------------------------------------------------- /db-migrations/15_project_label.up.sql: -------------------------------------------------------------------------------- 1 | -- Copyright 2020 The Merlin Authors 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | ALTER TABLE projects ADD COLUMN team VARCHAR(64), 16 | ADD COLUMN stream VARCHAR(64), 17 | ADD COLUMN labels jsonb; -------------------------------------------------------------------------------- /db-migrations/16_prediction_job_metadata.down.sql: -------------------------------------------------------------------------------- 1 | -- Copyright 2020 The Merlin Authors 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | ALTER TABLE prediction_jobs DROP COLUMN metadata; -------------------------------------------------------------------------------- /db-migrations/16_prediction_job_metadata.up.sql: -------------------------------------------------------------------------------- 1 | -- Copyright 2020 The Merlin Authors 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | ALTER TABLE prediction_jobs ADD COLUMN metadata jsonb; -------------------------------------------------------------------------------- /db-migrations/18_prediction_jobs_index.down.sql: -------------------------------------------------------------------------------- 1 | -- Copyright 2020 The Merlin Authors 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | DROP INDEX prediction_jobs_idx_1; 16 | -------------------------------------------------------------------------------- /db-migrations/18_prediction_jobs_index.up.sql: -------------------------------------------------------------------------------- 1 | -- Copyright 2020 The Merlin Authors 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | CREATE INDEX prediction_jobs_idx_1 ON prediction_jobs ( 16 | project_id, version_model_id 17 | ); 18 | -------------------------------------------------------------------------------- /db-migrations/19_transformer.down.sql: -------------------------------------------------------------------------------- 1 | -- Copyright 2020 The Merlin Authors 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | DROP TABLE IF EXISTS transformers CASCADE; 16 | -------------------------------------------------------------------------------- /db-migrations/21_logger_column_on_version_endpoints.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE version_endpoints DROP COLUMN logger; 2 | -------------------------------------------------------------------------------- /db-migrations/21_logger_column_on_version_endpoints.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE version_endpoints ADD COLUMN logger jsonb; 2 | -------------------------------------------------------------------------------- /db-migrations/22_standard_transformer.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE transformers DROP COLUMN transformer_type; 2 | ALTER TABLE environments DROP COLUMN default_transformer_resource_request; 3 | -------------------------------------------------------------------------------- /db-migrations/22_standard_transformer.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE transformers ADD COLUMN transformer_type VARCHAR(64); 2 | 3 | UPDATE transformers 4 | SET transformer_type = 'custom' 5 | WHERE transformer_type is null; 6 | 7 | ALTER TABLE environments ADD COLUMN default_transformer_resource_request jsonb; -------------------------------------------------------------------------------- /db-migrations/23_version_labels.down.sql: -------------------------------------------------------------------------------- 1 | DROP INDEX version_labels_idx; 2 | 3 | ALTER TABLE versions DROP COLUMN labels; 4 | -------------------------------------------------------------------------------- /db-migrations/23_version_labels.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE versions ADD COLUMN labels jsonb; 2 | 3 | CREATE INDEX version_labels_idx ON versions USING GIN (labels jsonb_path_ops); 4 | -------------------------------------------------------------------------------- /db-migrations/24_job.down.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE jobs; -------------------------------------------------------------------------------- /db-migrations/24_job.up.sql: -------------------------------------------------------------------------------- 1 | 2 | CREATE TABLE IF NOT EXISTS jobs ( 3 | id bigserial PRIMARY KEY, 4 | name varchar(255), 5 | arguments jsonb, 6 | completed boolean NOT NULL default false, 7 | created_at timestamp NOT NULL default current_timestamp, 8 | updated_at timestamp NOT NULL default current_timestamp 9 | ); 10 | 11 | CREATE INDEX jobs_name_idx ON jobs(name) WHERE completed is not true; -------------------------------------------------------------------------------- /db-migrations/25_message_varchar_length.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE version_endpoints ALTER COLUMN message TYPE varchar(64) USING SUBSTR(message, 1, 64); 2 | -------------------------------------------------------------------------------- /db-migrations/25_message_varchar_length.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE version_endpoints ALTER COLUMN message TYPE varchar(2048); 2 | -------------------------------------------------------------------------------- /db-migrations/26_custom_predictor_on_versions.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE versions 2 | DROP COLUMN custom_predictor; 3 | -------------------------------------------------------------------------------- /db-migrations/26_custom_predictor_on_versions.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE versions 2 | ADD COLUMN custom_predictor jsonb; 3 | -------------------------------------------------------------------------------- /db-migrations/27_deployment_mode.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE version_endpoints DROP COLUMN deployment_mode; 2 | DROP TYPE IF EXISTS deployment_mode; 3 | -------------------------------------------------------------------------------- /db-migrations/27_deployment_mode.up.sql: -------------------------------------------------------------------------------- 1 | CREATE TYPE deployment_mode as ENUM ('raw_deployment', 'serverless'); 2 | ALTER TABLE version_endpoints ADD COLUMN deployment_mode deployment_mode NOT NULL default 'serverless'; 3 | -------------------------------------------------------------------------------- /db-migrations/28_autoscaling_policy.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE version_endpoints DROP COLUMN autoscaling_policy; 2 | -------------------------------------------------------------------------------- /db-migrations/28_autoscaling_policy.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE version_endpoints ADD COLUMN autoscaling_policy jsonb; 2 | -------------------------------------------------------------------------------- /db-migrations/29_protocol.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE version_endpoints DROP COLUMN protocol; 2 | ALTER TABLE model_endpoints DROP COLUMN protocol; 3 | -------------------------------------------------------------------------------- /db-migrations/29_protocol.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE version_endpoints ADD COLUMN protocol VARCHAR(32); 2 | ALTER TABLE model_endpoints ADD COLUMN protocol VARCHAR(32); 3 | -------------------------------------------------------------------------------- /db-migrations/30_versions_python_version.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE versions DROP COLUMN python_version; -------------------------------------------------------------------------------- /db-migrations/30_versions_python_version.up.sql: -------------------------------------------------------------------------------- 1 | -- Introduce Python version column, with default as version 3.7 which is the 2 | -- only supported major version as of the introduction of the column. 3 | ALTER TABLE versions ADD COLUMN python_version varchar(16) NOT NULL DEFAULT '3.7.*'; -------------------------------------------------------------------------------- /db-migrations/31_environments_gpus.down.sql: -------------------------------------------------------------------------------- 1 | -- Description: Drop the gpus column from the environments table. 2 | ALTER TABLE environments DROP COLUMN gpus; 3 | 4 | -------------------------------------------------------------------------------- /db-migrations/31_environments_gpus.up.sql: -------------------------------------------------------------------------------- 1 | -- add gpus column to environments table 2 | ALTER TABLE environments ADD COLUMN gpus jsonb; -------------------------------------------------------------------------------- /db-migrations/32_revision_id.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE version_endpoints DROP COLUMN revision_id; 2 | -------------------------------------------------------------------------------- /db-migrations/32_revision_id.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE version_endpoints 2 | ADD COLUMN revision_id VARCHAR(32); 3 | -------------------------------------------------------------------------------- /db-migrations/33_update_multiple_deployments.down.sql: -------------------------------------------------------------------------------- 1 | -- Do nothing 2 | -------------------------------------------------------------------------------- /db-migrations/34_version_endpoints_enable_observability.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE version_endpoints 2 | DROP COLUMN enable_model_observability; 3 | -------------------------------------------------------------------------------- /db-migrations/34_version_endpoints_enable_observability.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE version_endpoints 2 | ADD COLUMN enable_model_observability BOOLEAN NOT NULL DEFAULT false; 3 | -------------------------------------------------------------------------------- /db-migrations/35_image_builder.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE version_endpoints DROP COLUMN image_builder_resource_request; 2 | -------------------------------------------------------------------------------- /db-migrations/35_image_builder.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE version_endpoints 2 | ADD COLUMN image_builder_resource_request jsonb; 3 | -------------------------------------------------------------------------------- /db-migrations/36_model_schemas.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE versions DROP column model_schema_id; 2 | DROP TABLE model_schemas; -------------------------------------------------------------------------------- /db-migrations/36_model_schemas.up.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS model_schemas 2 | ( 3 | id serial PRIMARY KEY, 4 | model_id integer, 5 | spec JSONB NOT NULL, 6 | created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 7 | updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 8 | CONSTRAINT model_schemas_model_fkey 9 | FOREIGN KEY (model_id) REFERENCES models (id) 10 | ); 11 | 12 | CREATE INDEX model_schemas_model_id_idx ON model_schemas(model_id); 13 | ALTER TABLE versions ADD COLUMN model_schema_id integer REFERENCES model_schemas (id); -------------------------------------------------------------------------------- /db-migrations/37_environments_add_max_allowed_replicas.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE environments DROP COLUMN max_allowed_replica; 2 | -------------------------------------------------------------------------------- /db-migrations/37_environments_add_max_allowed_replicas.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE environments 2 | ADD COLUMN max_allowed_replica int; 3 | -------------------------------------------------------------------------------- /db-migrations/38_supported_observability_on_models.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE models DROP COLUMN observability_supported; -------------------------------------------------------------------------------- /db-migrations/38_supported_observability_on_models.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE models ADD COLUMN observability_supported BOOLEAN NOT NULL DEFAULT false; -------------------------------------------------------------------------------- /db-migrations/39_observability_publisher.down.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE observability_publishers; 2 | DROP TYPE publisher_status; -------------------------------------------------------------------------------- /db-migrations/39_observability_publisher.up.sql: -------------------------------------------------------------------------------- 1 | CREATE TYPE publisher_status as ENUM ('pending', 'running', 'failed', 'terminated'); 2 | 3 | CREATE TABLE IF NOT EXISTS observability_publishers 4 | ( 5 | id serial PRIMARY KEY, 6 | version_model_id integer, 7 | version_id integer, 8 | revision integer, 9 | status publisher_status, 10 | model_schema_spec JSONB, 11 | created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 12 | updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 13 | UNIQUE(version_model_id) 14 | ); -------------------------------------------------------------------------------- /db-migrations/3_deployment_status.down.sql: -------------------------------------------------------------------------------- 1 | -- Copyright 2020 The Merlin Authors 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | ALTER TABLE version_endpoints DROP COLUMN message; -------------------------------------------------------------------------------- /db-migrations/3_deployment_status.up.sql: -------------------------------------------------------------------------------- 1 | -- Copyright 2020 The Merlin Authors 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | ALTER TABLE version_endpoints ADD COLUMN message varchar(64); -------------------------------------------------------------------------------- /db-migrations/40_model_observability_on_version_endpoints.down.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE version_endpoints DROP COLUMN model_observability; 2 | -------------------------------------------------------------------------------- /db-migrations/40_model_observability_on_version_endpoints.up.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE version_endpoints ADD COLUMN model_observability jsonb; -------------------------------------------------------------------------------- /db-migrations/41_version_endpoints_add_secrets.down.sql: -------------------------------------------------------------------------------- 1 | -- Copyright 2020 The Merlin Authors 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | ALTER TABLE version_endpoints DROP COLUMN secrets; 16 | 17 | ALTER TABLE transformers DROP COLUMN secrets; 18 | -------------------------------------------------------------------------------- /db-migrations/4_env_add_columns.down.sql: -------------------------------------------------------------------------------- 1 | -- Copyright 2020 The Merlin Authors 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | ALTER TABLE environments DROP COLUMN region; 16 | ALTER TABLE environments DROP COLUMN gcp_project; 17 | -------------------------------------------------------------------------------- /db-migrations/4_env_add_columns.up.sql: -------------------------------------------------------------------------------- 1 | -- Copyright 2020 The Merlin Authors 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | ALTER TABLE environments ADD COLUMN region varchar(2); 16 | ALTER TABLE environments ADD COLUMN gcp_project varchar(64); 17 | -------------------------------------------------------------------------------- /db-migrations/5_delete_triggers.down.sql: -------------------------------------------------------------------------------- 1 | -- Copyright 2020 The Merlin Authors 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | CREATE TABLE IF NOT EXISTS triggers; 16 | -------------------------------------------------------------------------------- /db-migrations/5_delete_triggers.up.sql: -------------------------------------------------------------------------------- 1 | -- Copyright 2020 The Merlin Authors 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | DROP TABLE IF EXISTS triggers; 16 | -------------------------------------------------------------------------------- /db-migrations/6_resource_request.down.sql: -------------------------------------------------------------------------------- 1 | -- Copyright 2020 The Merlin Authors 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | ALTER TABLE version_endpoints DROP COLUMN resource_request; 16 | ALTER TABLE environments DROP COLUMN default_resource_request; -------------------------------------------------------------------------------- /db-migrations/7_env_var.down.sql: -------------------------------------------------------------------------------- 1 | -- Copyright 2020 The Merlin Authors 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | ALTER TABLE version_endpoints DROP COLUMN env_vars; 16 | -------------------------------------------------------------------------------- /db-migrations/7_env_var.up.sql: -------------------------------------------------------------------------------- 1 | -- Copyright 2020 The Merlin Authors 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | ALTER TABLE version_endpoints ADD COLUMN env_vars jsonb; 16 | -------------------------------------------------------------------------------- /db-migrations/8_authz.down.sql: -------------------------------------------------------------------------------- 1 | -- Copyright 2020 The Merlin Authors 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | ALTER TABLE projects DROP COLUMN administrators; 16 | ALTER TABLE projects DROP COLUMN readers; -------------------------------------------------------------------------------- /db-migrations/8_authz.up.sql: -------------------------------------------------------------------------------- 1 | -- Copyright 2020 The Merlin Authors 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | ALTER TABLE projects ADD COLUMN administrators varchar(256)[]; 16 | ALTER TABLE projects ADD COLUMN readers varchar(256)[]; -------------------------------------------------------------------------------- /db-migrations/9_secret.down.sql: -------------------------------------------------------------------------------- 1 | -- Copyright 2020 The Merlin Authors 2 | -- 3 | -- Licensed under the Apache License, Version 2.0 (the "License"); 4 | -- you may not use this file except in compliance with the License. 5 | -- You may obtain a copy of the License at 6 | -- 7 | -- http://www.apache.org/licenses/LICENSE-2.0 8 | -- 9 | -- Unless required by applicable law or agreed to in writing, software 10 | -- distributed under the License is distributed on an "AS IS" BASIS, 11 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -- See the License for the specific language governing permissions and 13 | -- limitations under the License. 14 | 15 | DROP TABLE secrets; -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | mdformatter/ -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: docs 2 | docs: setup format 3 | 4 | .PHONY: setup 5 | setup: 6 | @rm -rf mdformatter 7 | @git clone https://github.com/caraml-dev/mdformatter.git 8 | @pip install -r mdformatter/requirements.txt 9 | 10 | # The target below uses a non-existent doc overrides folder name to generate the final docs, 11 | # as there are no overrides. 12 | .PHONY: format 13 | format: 14 | @echo "Formatting maintainer docs ..." 15 | @cd mdformatter && python -m mdformatter ../maintainer/templates ../maintainer/overrides ../maintainer/generated ../maintainer/values.json GITBOOK 16 | @echo "Formatting user docs ..." 17 | @cd mdformatter && python -m mdformatter ../user/templates ../user/overrides ../user/generated ../user/values.json GITBOOK 18 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Docs 2 | 3 | To learn about the basic concepts behind Merlin and how to use it, refer to the [User Docs](./user/generated). 4 | 5 | To configure / deploy Merlin into a production cluster or troubleshoot an existing deployment, refer to the [Maintainer Docs](./maintainer). 6 | 7 | To understand the development process and the architecture, refer to the [Developer Docs](./developer). 8 | 9 | ## Contributing to the Docs 10 | 11 | All docs are created for Gitbook. 12 | 13 | Currently, the user docs and maintainer docs are templated using Jinja2. 14 | 15 | The templates can be found under `${folder}/templates` and the values for the templates reside in `${folder}/values.json`. To generate the final docs into `${folder}/generated`, run: 16 | 17 | ```sh 18 | make docs 19 | ``` 20 | -------------------------------------------------------------------------------- /docs/images/autoscaling_policy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/docs/images/autoscaling_policy.png -------------------------------------------------------------------------------- /docs/images/batch_resource_configuration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/docs/images/batch_resource_configuration.png -------------------------------------------------------------------------------- /docs/images/batch_resource_metrics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/docs/images/batch_resource_metrics.png -------------------------------------------------------------------------------- /docs/images/configure_alert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/docs/images/configure_alert.png -------------------------------------------------------------------------------- /docs/images/configure_alert_models_list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/docs/images/configure_alert_models_list.png -------------------------------------------------------------------------------- /docs/images/configure_standard_transformer.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/docs/images/configure_standard_transformer.gif -------------------------------------------------------------------------------- /docs/images/delete_model_active_entity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/docs/images/delete_model_active_entity.png -------------------------------------------------------------------------------- /docs/images/delete_model_no_entity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/docs/images/delete_model_no_entity.png -------------------------------------------------------------------------------- /docs/images/delete_model_version_active_entity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/docs/images/delete_model_version_active_entity.png -------------------------------------------------------------------------------- /docs/images/delete_model_version_inactive_entity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/docs/images/delete_model_version_inactive_entity.png -------------------------------------------------------------------------------- /docs/images/delete_model_version_no_entity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/docs/images/delete_model_version_no_entity.png -------------------------------------------------------------------------------- /docs/images/deploy_model_version.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/docs/images/deploy_model_version.png -------------------------------------------------------------------------------- /docs/images/deployment_errors/crashloopbackoff.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/docs/images/deployment_errors/crashloopbackoff.png -------------------------------------------------------------------------------- /docs/images/deployment_errors/image_building_oomkilled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/docs/images/deployment_errors/image_building_oomkilled.png -------------------------------------------------------------------------------- /docs/images/deployment_errors/image_not_found.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/docs/images/deployment_errors/image_not_found.png -------------------------------------------------------------------------------- /docs/images/deployment_errors/version_log_history_tab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/docs/images/deployment_errors/version_log_history_tab.png -------------------------------------------------------------------------------- /docs/images/deployment_mode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/docs/images/deployment_mode.png -------------------------------------------------------------------------------- /docs/images/merlin-image-only.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/docs/images/merlin-image-only.png -------------------------------------------------------------------------------- /docs/images/merlin-with-text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/docs/images/merlin-with-text.png -------------------------------------------------------------------------------- /docs/images/override_cpu_limits.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/docs/images/override_cpu_limits.png -------------------------------------------------------------------------------- /docs/images/redeploy_model_unsuccessful.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/docs/images/redeploy_model_unsuccessful.png -------------------------------------------------------------------------------- /docs/images/redeploy_model_version.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/docs/images/redeploy_model_version.png -------------------------------------------------------------------------------- /docs/images/serve_model_version.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/docs/images/serve_model_version.png -------------------------------------------------------------------------------- /docs/images/standard_transformer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/docs/images/standard_transformer.png -------------------------------------------------------------------------------- /docs/images/upi_autoloading_config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/docs/images/upi_autoloading_config.png -------------------------------------------------------------------------------- /docs/images/upi_preprocess_output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/docs/images/upi_preprocess_output.png -------------------------------------------------------------------------------- /docs/maintainer/values.json: -------------------------------------------------------------------------------- 1 | { 2 | "merlin_cluster_name": "caraml-cluster", 3 | "merlin_namespace_name": "caraml-namespace" 4 | } -------------------------------------------------------------------------------- /docs/user/generated/03_deploying_a_model.md: -------------------------------------------------------------------------------- 1 | 2 | # Deploying a Model 3 | 4 | To learn about deploying a model, please visit the following docs. 5 | 6 | {% page-ref page="./model_deployment/01_deploying_a_model_version.md" %} 7 | 8 | {% page-ref page="./model_deployment/02_serving_a_model_version.md" %} 9 | 10 | {% page-ref page="./model_deployment/03_configuring_transformers.md" %} 11 | 12 | {% page-ref page="./model_deployment/04_redeploying_a_model_version.md" %} -------------------------------------------------------------------------------- /docs/user/generated/07_examples.md: -------------------------------------------------------------------------------- 1 | 2 | # Examples 3 | 4 | Examples of using Merlin for different purposes are available to be tried out as Jupyter notebooks in the links below. 5 | You may want to clone the examples to your local directory and run them using Jupyter notebook. 6 | 7 | {% page-ref page="./examples/01_standard_model.md" %} 8 | 9 | {% page-ref page="./examples/02_pyfunc_model.md" %} 10 | 11 | {% page-ref page="./examples/03_transformer.md" %} 12 | 13 | {% page-ref page="./examples/04_batch_prediction.md" %} 14 | 15 | {% page-ref page="./examples/05_others.md" %} -------------------------------------------------------------------------------- /docs/user/generated/12_build_image.md: -------------------------------------------------------------------------------- 1 | # Building Image of a Model Version 2 | 3 | Image building is a step before deployment that wraps the user's PyFunc model, artifacts, and dependencies into a Docker image. This Docker image then can be deployed to serve upcoming inference requests. 4 | 5 | Merlin provides SDK to programmatically start the image building process without actually deploying the model version. 6 | 7 | ```python 8 | with merlin.new_model_version() as v: 9 | v.log_pyfunc_model( 10 | model_instance=MyPyFuncModel(), 11 | conda_env="env.yaml", 12 | ) 13 | 14 | v.build_image() 15 | ``` -------------------------------------------------------------------------------- /docs/user/generated/examples/04_batch_prediction.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Run Batch Prediction Job 4 | 5 | Try out the notebooks below to learn how to run batch prediction jobs using PyFunc V2 in Merlin. 6 | 7 | ## Run Iris Classifier Batch Prediction Job 8 | 9 | {% embed url="https://github.com/caraml-dev/merlin/blob/main/examples/batch/BatchPredictionTutorial1-IrisClassifier.ipynb" %} 10 | 11 | ## Run New York Taxi Fare Batch Prediction Job 12 | 13 | {% embed url="https://github.com/caraml-dev/merlin/blob/main/examples/batch/BatchPredictionTutorial2-NewYorkTaxi.ipynb" %} -------------------------------------------------------------------------------- /docs/user/generated/examples/05_others.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Others 4 | 5 | Try out the notebooks below to learn about other features of Merlin. 6 | 7 | ## Requesting CPU and Memory Resources 8 | 9 | {% embed url="https://github.com/caraml-dev/merlin/blob/main/examples/resource-request/Resource-Request.ipynb" %} 10 | 11 | ## Working with Model Endpoint 12 | 13 | {% embed url="https://github.com/caraml-dev/merlin/blob/main/examples/model-endpoint/ModelEndpoint.ipynb" %} -------------------------------------------------------------------------------- /docs/user/generated/model_deployment/03_configuring_transformers.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Transformer 4 | 5 | In the Merlin ecosystem, a Transformer is a service deployed in front of the model service which users can use to perform pre-processing / post-processing steps to the incoming request / outgoing response, to / from the model service. A Transformer allows the user to abstract the transformation logic outside of their model and even write it in a language more performant than python. 6 | 7 | Currently, Merlin supports two types of Transformer: Standard and Custom: 8 | 9 | {% page-ref page="./transformer/01_standard_transformer.md" %} 10 | 11 | {% page-ref page="./transformer/02_custom_transformer.md" %} -------------------------------------------------------------------------------- /docs/user/templates/03_deploying_a_model.md: -------------------------------------------------------------------------------- 1 | 2 | # Deploying a Model 3 | 4 | To learn about deploying a model, please visit the following docs. 5 | 6 | {% page-ref page="./model_deployment/01_deploying_a_model_version.md" %} 7 | 8 | {% page-ref page="./model_deployment/02_serving_a_model_version.md" %} 9 | 10 | {% page-ref page="./model_deployment/03_configuring_transformers.md" %} 11 | 12 | {% page-ref page="./model_deployment/04_redeploying_a_model_version.md" %} -------------------------------------------------------------------------------- /docs/user/templates/07_examples.md: -------------------------------------------------------------------------------- 1 | 2 | # Examples 3 | 4 | Examples of using Merlin for different purposes are available to be tried out as Jupyter notebooks in the links below. 5 | You may want to clone the examples to your local directory and run them using Jupyter notebook. 6 | 7 | {% page-ref page="./examples/01_standard_model.md" %} 8 | 9 | {% page-ref page="./examples/02_pyfunc_model.md" %} 10 | 11 | {% page-ref page="./examples/03_transformer.md" %} 12 | 13 | {% page-ref page="./examples/04_batch_prediction.md" %} 14 | 15 | {% page-ref page="./examples/05_others.md" %} -------------------------------------------------------------------------------- /docs/user/templates/12_build_image.md: -------------------------------------------------------------------------------- 1 | # Building Image of a Model Version 2 | 3 | Image building is a step before deployment that wraps the user's PyFunc model, artifacts, and dependencies into a Docker image. This Docker image then can be deployed to serve upcoming inference requests. 4 | 5 | Merlin provides SDK to programmatically start the image building process without actually deploying the model version. 6 | 7 | ```python 8 | with merlin.new_model_version() as v: 9 | v.log_pyfunc_model( 10 | model_instance=MyPyFuncModel(), 11 | conda_env="env.yaml", 12 | ) 13 | 14 | v.build_image() 15 | ``` 16 | -------------------------------------------------------------------------------- /docs/user/templates/examples/04_batch_prediction.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Run Batch Prediction Job 4 | 5 | Try out the notebooks below to learn how to run batch prediction jobs using PyFunc V2 in Merlin. 6 | 7 | ## Run Iris Classifier Batch Prediction Job 8 | 9 | {% embed url="https://github.com/caraml-dev/merlin/blob/main/examples/batch/BatchPredictionTutorial1-IrisClassifier.ipynb" %} 10 | 11 | ## Run New York Taxi Fare Batch Prediction Job 12 | 13 | {% embed url="https://github.com/caraml-dev/merlin/blob/main/examples/batch/BatchPredictionTutorial2-NewYorkTaxi.ipynb" %} -------------------------------------------------------------------------------- /docs/user/templates/examples/05_others.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Others 4 | 5 | Try out the notebooks below to learn about other features of Merlin. 6 | 7 | ## Requesting CPU and Memory Resources 8 | 9 | {% embed url="https://github.com/caraml-dev/merlin/blob/main/examples/resource-request/Resource-Request.ipynb" %} 10 | 11 | ## Working with Model Endpoint 12 | 13 | {% embed url="https://github.com/caraml-dev/merlin/blob/main/examples/model-endpoint/ModelEndpoint.ipynb" %} 14 | -------------------------------------------------------------------------------- /docs/user/templates/model_deployment/03_configuring_transformers.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Transformer 4 | 5 | In the Merlin ecosystem, a Transformer is a service deployed in front of the model service which users can use to perform pre-processing / post-processing steps to the incoming request / outgoing response, to / from the model service. A Transformer allows the user to abstract the transformation logic outside of their model and even write it in a language more performant than python. 6 | 7 | Currently, Merlin supports two types of Transformer: Standard and Custom: 8 | 9 | {% page-ref page="./transformer/01_standard_transformer.md" %} 10 | 11 | {% page-ref page="./transformer/02_custom_transformer.md" %} -------------------------------------------------------------------------------- /docs/user/values.json: -------------------------------------------------------------------------------- 1 | { 2 | "merlin_url": "merlin.example.com", 3 | "models_base_domain": "models.id.merlin.dev", 4 | "workflow_scope_explaination": "" 5 | } -------------------------------------------------------------------------------- /examples/batch/env.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - pip: 3 | - joblib>=0.13.0,<1.2.0 # >=1.2.0 upon upgrade of kserve's version 4 | - numpy>=1.19.5 5 | - scikit-learn>=1.1.2 6 | - xgboost==1.6.2 7 | - mlflow==1.26.1 -------------------------------------------------------------------------------- /examples/batch/model/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/examples/batch/model/.keep -------------------------------------------------------------------------------- /examples/batch/model/model.joblib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/examples/batch/model/model.joblib -------------------------------------------------------------------------------- /examples/batch/model/nyc-model.joblib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/examples/batch/model/nyc-model.joblib -------------------------------------------------------------------------------- /examples/batch/requirements.txt: -------------------------------------------------------------------------------- 1 | joblib>=0.13.0,<1.2.0 # >=1.2.0 upon upgrade of kserve's version 2 | numpy>=1.19.5 3 | scikit-learn>=1.1.2 4 | xgboost==1.6.2 5 | mlflow==1.26.1 6 | merlin-sdk 7 | cloudpickle==2.0.0 8 | google-cloud 9 | google-cloud-bigquery[pandas]==2.34.3 -------------------------------------------------------------------------------- /examples/custom-model/fastapi/requirements.txt: -------------------------------------------------------------------------------- 1 | scikit-learn==1.1.2 2 | xgboost==1.6.2 3 | merlin-sdk -------------------------------------------------------------------------------- /examples/custom-model/fastapi/server/boot.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Get the value of the MERLIN_PREDICTOR_PORT environment variable 4 | # Get the value of the MERLIN_GUNICORN_WORKERS environment variable 5 | port="$MERLIN_PREDICTOR_PORT" 6 | workers="$WORKERS" 7 | 8 | # If the port environment variable is not set, use the default port 8080 9 | # If the workers environment variable is not set, use the default worker number 1 10 | 11 | if [ -z "$port" ]; then 12 | port="8080" 13 | fi 14 | 15 | if [ -z "$workers" ]; then 16 | workers="1" 17 | fi 18 | 19 | # Execute the Gunicorn command with the specified port and number of workers 20 | exec uvicorn app:app --host=0.0.0.0 --port=$port --workers=$workers --no-access-log -------------------------------------------------------------------------------- /examples/custom-model/fastapi/server/dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.10-slim 2 | 3 | WORKDIR /app 4 | 5 | # Install dependencies 6 | RUN pip install --upgrade pip 7 | RUN pip install fastapi uvicorn requests 8 | RUN pip install xgboost==1.6.2 9 | 10 | # Copy source code 11 | COPY . . 12 | 13 | # Add execute permission to boot.sh 14 | RUN chmod +x boot.sh 15 | 16 | ENTRYPOINT ["./boot.sh"] 17 | -------------------------------------------------------------------------------- /examples/custom-model/http_json/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.22-alpine as go-builder 2 | 3 | RUN apk update && apk add --no-cache git ca-certificates bash 4 | RUN mkdir -p src 5 | 6 | WORKDIR /src 7 | COPY model-server . 8 | 9 | RUN go build -o bin/custom_model 10 | 11 | 12 | FROM alpine:3.12 13 | 14 | COPY --from=go-builder /src/bin/custom_model /usr/bin/custom_model 15 | CMD ["custom_model"] 16 | -------------------------------------------------------------------------------- /examples/custom-model/http_json/model-server/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/gojek/custom-model 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/dmitryikh/leaves v0.0.0-20210121075304-82771f84c313 7 | github.com/golang/protobuf v1.4.3 // indirect 8 | github.com/gorilla/mux v1.8.0 9 | github.com/heptiolabs/healthcheck v0.0.0-20180807145615-6ff867650f40 10 | github.com/kelseyhightower/envconfig v1.4.0 11 | github.com/prometheus/client_golang v1.6.0 12 | github.com/prometheus/common v0.9.1 13 | github.com/prometheus/procfs v0.6.0 // indirect 14 | github.com/stretchr/testify v1.7.0 // indirect 15 | golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 // indirect 16 | google.golang.org/protobuf v1.26.0-rc.1 // indirect 17 | gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0 // indirect 18 | ) 19 | -------------------------------------------------------------------------------- /examples/custom-model/http_json/model-server/model/payload.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type Request struct { 4 | Instances [][]float64 `json:"instances"` 5 | } 6 | 7 | type Response struct { 8 | Predictions []float64 `json:"predictions"` 9 | } 10 | -------------------------------------------------------------------------------- /examples/custom-model/http_json/requirements.txt: -------------------------------------------------------------------------------- 1 | scikit-learn>=1.1.2 2 | xgboost==1.6.2 3 | merlin-sdk -------------------------------------------------------------------------------- /examples/custom-model/upi_v1/Dockerfile: -------------------------------------------------------------------------------- 1 | # ============================================================ 2 | # Build stage 1: Build web server 3 | # ============================================================ 4 | FROM golang:1.22-alpine as go-builder 5 | 6 | RUN mkdir -p /src/model 7 | WORKDIR /src/model 8 | 9 | COPY . . 10 | 11 | RUN go build -o bin/model-server cmd/main.go 12 | 13 | 14 | ## ============================================================ 15 | ## Build stage 2: Copy binary 16 | ## ============================================================ 17 | # 18 | FROM debian:buster-slim 19 | 20 | COPY --from=go-builder /src/model/bin/model-server /usr/bin/model-server 21 | 22 | ENTRYPOINT ["model-server"] 23 | -------------------------------------------------------------------------------- /examples/custom-model/upi_v1/cmd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "custom-model-upi/pkg/server" 5 | 6 | "github.com/kelseyhightower/envconfig" 7 | "go.uber.org/zap" 8 | 9 | "github.com/caraml-dev/merlin/log" 10 | ) 11 | 12 | func main() { 13 | cfg := server.Config{} 14 | if err := envconfig.Process("", &cfg); err != nil { 15 | log.Panicf(err.Error()) 16 | } 17 | logger, _ := zap.NewProduction() 18 | defer logger.Sync() // flushes buffer, if any 19 | 20 | instrumentRouter := server.NewInstrumentationRouter() 21 | server.RunUPIServer(&cfg, instrumentRouter, logger) 22 | } 23 | -------------------------------------------------------------------------------- /examples/custom-model/upi_v1/pkg/interceptors/recoverer.go: -------------------------------------------------------------------------------- 1 | package interceptors 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "google.golang.org/grpc" 8 | ) 9 | 10 | // PanicRecoveryInterceptor interceptor to recover after facing panic 11 | func PanicRecoveryInterceptor() grpc.UnaryServerInterceptor { 12 | return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { 13 | defer func(ctx context.Context) { 14 | if r := recover(); r != nil { 15 | if e, ok := r.(error); ok { 16 | err = e 17 | } else { 18 | err = fmt.Errorf("panic: %s", r) 19 | } 20 | } 21 | }(ctx) 22 | 23 | resp, err = handler(ctx, req) 24 | return resp, err 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/custom-model/upi_v1/requirements.txt: -------------------------------------------------------------------------------- 1 | merlin-sdk 2 | caraml-upi-protos>=0.3.1 3 | pandas -------------------------------------------------------------------------------- /examples/metrics/env.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - python=3.9 -------------------------------------------------------------------------------- /examples/metrics/requirements.txt: -------------------------------------------------------------------------------- 1 | prometheus_client 2 | merlin-sdk 3 | cloudpickle==2.0.0 -------------------------------------------------------------------------------- /examples/model-endpoint/requirements.txt: -------------------------------------------------------------------------------- 1 | scikit-learn>=1.1.2 2 | merlin-sdk 3 | joblib>=0.13.0,<1.2.0 # >=1.2.0 upon upgrade of kserve's version 4 | cloudpickle==2.0.0 -------------------------------------------------------------------------------- /examples/model-endpoint/sklearn-model/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/examples/model-endpoint/sklearn-model/.keep -------------------------------------------------------------------------------- /examples/model-endpoint/sklearn-model/model.joblib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/examples/model-endpoint/sklearn-model/model.joblib -------------------------------------------------------------------------------- /examples/pyfunc-upi/env.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - python=3.9 3 | - pip: 4 | - numpy 5 | - xgboost==1.6.2 -------------------------------------------------------------------------------- /examples/pyfunc-upi/requirements.txt: -------------------------------------------------------------------------------- 1 | xgboost==1.6.2 2 | scikit-learn==1.0.2 #TODO: >=1.1.2 upon python 3.7 deprecation 3 | merlin-sdk>=0.22.2rc4 4 | numpy -------------------------------------------------------------------------------- /examples/pyfunc-upi/xgboost-model/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/examples/pyfunc-upi/xgboost-model/.keep -------------------------------------------------------------------------------- /examples/pyfunc/env.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - python=3.9 3 | - pip: 4 | - joblib>=0.13.0,<1.2.0 # >=1.2.0 upon upgrade of kserve's version 5 | - scikit-learn>=1.1.2 6 | - xgboost==1.6.2 7 | -------------------------------------------------------------------------------- /examples/pyfunc/requirements.txt: -------------------------------------------------------------------------------- 1 | scikit-learn>=1.1.2 2 | joblib>=0.13.0,<1.2.0 # >=1.2.0 upon upgrade of kserve's version 3 | xgboost==1.6.2 4 | merlin-sdk 5 | numpy 6 | cloudpickle==2.0.0 -------------------------------------------------------------------------------- /examples/pyfunc/sklearn-model/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/examples/pyfunc/sklearn-model/.keep -------------------------------------------------------------------------------- /examples/pyfunc/sklearn-model/model_2.joblib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/examples/pyfunc/sklearn-model/model_2.joblib -------------------------------------------------------------------------------- /examples/pyfunc/xgboost-model/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/examples/pyfunc/xgboost-model/.keep -------------------------------------------------------------------------------- /examples/pyfunc/xgboost-model/model_1.bst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/examples/pyfunc/xgboost-model/model_1.bst -------------------------------------------------------------------------------- /examples/pytorch/pytorch-model/config/config.properties: -------------------------------------------------------------------------------- 1 | inference_address=http://0.0.0.0:8085 2 | management_address=http://0.0.0.0:8085 3 | metrics_address=http://0.0.0.0:8082 4 | grpc_inference_port=7070 5 | grpc_management_port=7071 6 | enable_metrics_api=true 7 | metrics_format=prometheus 8 | number_of_netty_threads=4 9 | job_queue_size=10 10 | enable_envvars_config=true 11 | install_py_dep_per_model=true 12 | model_store=/mnt/models/model-store 13 | model_snapshot={"name":"startup.cfg","modelCount":1,"models":{"pytorch-sample-16":{"1.0":{"defaultVersion":true,"marName":"model.mar","minWorkers":1,"maxWorkers":5,"batchSize":1,"maxBatchDelay":10,"responseTimeout":120}}}} -------------------------------------------------------------------------------- /examples/pytorch/pytorch-model/iris_handler.py: -------------------------------------------------------------------------------- 1 | from ts.torch_handler.base_handler import BaseHandler 2 | 3 | class IrisHandler(BaseHandler): 4 | pass -------------------------------------------------------------------------------- /examples/pytorch/pytorch-model/model.py: -------------------------------------------------------------------------------- 1 | import torch.nn as nn 2 | import torch.nn.functional as F 3 | 4 | class PyTorchModel(nn.Module): 5 | # define nn 6 | def __init__(self): 7 | super(PyTorchModel, self).__init__() 8 | self.fc1 = nn.Linear(4, 100) 9 | self.fc2 = nn.Linear(100, 100) 10 | self.fc3 = nn.Linear(100, 3) 11 | self.softmax = nn.Softmax(dim=1) 12 | 13 | def forward(self, X): 14 | X = F.relu(self.fc1(X)) 15 | X = self.fc2(X) 16 | X = self.fc3(X) 17 | X = self.softmax(X) 18 | 19 | return X -------------------------------------------------------------------------------- /examples/pytorch/requirements.txt: -------------------------------------------------------------------------------- 1 | sklearn 2 | torch 3 | merlin-sdk 4 | cloudpickle==2.0.0 -------------------------------------------------------------------------------- /examples/resource-request-gpu/requirements.txt: -------------------------------------------------------------------------------- 1 | scikit-learn>=1.1.2 2 | merlin-sdk 3 | joblib>=0.13.0,<1.2.0 # >=1.2.0 upon upgrade of kserve's version 4 | cloudpickle==2.0.0 -------------------------------------------------------------------------------- /examples/resource-request-gpu/sklearn-model/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/examples/resource-request-gpu/sklearn-model/.keep -------------------------------------------------------------------------------- /examples/resource-request-gpu/sklearn-model/model.joblib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/examples/resource-request-gpu/sklearn-model/model.joblib -------------------------------------------------------------------------------- /examples/resource-request/requirements.txt: -------------------------------------------------------------------------------- 1 | scikit-learn>=1.1.2 2 | merlin-sdk 3 | joblib>=0.13.0,<1.2.0 # >=1.2.0 upon upgrade of kserve's version 4 | cloudpickle==2.0.0 -------------------------------------------------------------------------------- /examples/resource-request/sklearn-model/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/examples/resource-request/sklearn-model/.keep -------------------------------------------------------------------------------- /examples/resource-request/sklearn-model/model.joblib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/examples/resource-request/sklearn-model/model.joblib -------------------------------------------------------------------------------- /examples/sklearn/requirements.txt: -------------------------------------------------------------------------------- 1 | scikit-learn>=1.1.2 2 | merlin-sdk 3 | joblib>=0.13.0,<1.2.0 # >=1.2.0 upon upgrade of kserve's version 4 | cloudpickle==2.0.0 -------------------------------------------------------------------------------- /examples/sklearn/sklearn-model/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/examples/sklearn/sklearn-model/.keep -------------------------------------------------------------------------------- /examples/tensorflow/requirements.txt: -------------------------------------------------------------------------------- 1 | merlin-sdk 2 | tensorflow==1.15 3 | pandas 4 | cloudpickle==2.0.0 -------------------------------------------------------------------------------- /examples/transformer/.gitignore: -------------------------------------------------------------------------------- 1 | data 2 | mlruns 3 | 4 | __pycache__ 5 | -------------------------------------------------------------------------------- /examples/transformer/custom-transformer/env.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - python=3.9 3 | - pip: 4 | - torch==1.3.1 5 | - torchvision==0.4.2 -------------------------------------------------------------------------------- /examples/transformer/custom-transformer/pytorch-model/model.pt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/examples/transformer/custom-transformer/pytorch-model/model.pt -------------------------------------------------------------------------------- /examples/transformer/custom-transformer/pytorch-model/model.py: -------------------------------------------------------------------------------- 1 | import torch.nn as nn 2 | import torch.nn.functional as F 3 | 4 | class PyTorchModel(nn.Module): 5 | def __init__(self): 6 | super(PyTorchModel, self).__init__() 7 | self.conv1 = nn.Conv2d(3, 6, 5) 8 | self.pool = nn.MaxPool2d(2, 2) 9 | self.conv2 = nn.Conv2d(6, 16, 5) 10 | self.fc1 = nn.Linear(16 * 5 * 5, 120) 11 | self.fc2 = nn.Linear(120, 84) 12 | self.fc3 = nn.Linear(84, 10) 13 | 14 | def forward(self, x): 15 | x = self.pool(F.relu(self.conv1(x))) 16 | x = self.pool(F.relu(self.conv2(x))) 17 | x = x.view(-1, 16 * 5 * 5) 18 | x = F.relu(self.fc1(x)) 19 | x = F.relu(self.fc2(x)) 20 | x = self.fc3(x) 21 | return x 22 | -------------------------------------------------------------------------------- /examples/transformer/custom-transformer/requirements.txt: -------------------------------------------------------------------------------- 1 | merlin-sdk 2 | pandas 3 | torch 4 | torchvision 5 | cloudpickle==2.0.0 -------------------------------------------------------------------------------- /examples/transformer/feast-enricher-transformer/env.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - python=3.9 -------------------------------------------------------------------------------- /examples/transformer/feast-enricher-transformer/requirements.txt: -------------------------------------------------------------------------------- 1 | merlin-sdk 2 | pandas 3 | cloudpickle==2.0.0 -------------------------------------------------------------------------------- /examples/transformer/standard-transformer/env.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - python=3.9 3 | -------------------------------------------------------------------------------- /examples/transformer/standard-transformer/requirements.txt: -------------------------------------------------------------------------------- 1 | merlin-sdk 2 | pandas 3 | cloudpickle==2.0.0 -------------------------------------------------------------------------------- /examples/transformer/upi/env.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - python=3.9 3 | -------------------------------------------------------------------------------- /examples/transformer/upi/requirements.txt: -------------------------------------------------------------------------------- 1 | pandas 2 | cloudpickle==2.0.0 3 | merlin-sdk -------------------------------------------------------------------------------- /examples/xgboost/requirements.txt: -------------------------------------------------------------------------------- 1 | scikit-learn>=1.1.2 2 | xgboost==1.6.2 3 | merlin-sdk 4 | cloudpickle==2.0.0 -------------------------------------------------------------------------------- /examples/xgboost/xgboost-model/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/examples/xgboost/xgboost-model/.keep -------------------------------------------------------------------------------- /examples/xgboost/xgboost-model/model.bst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/examples/xgboost/xgboost-model/model.bst -------------------------------------------------------------------------------- /infra/kafka/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: setup-kafka 2 | setup-kafka: 3 | @docker-compose up -d 4 | -------------------------------------------------------------------------------- /infra/kafka/update_run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Docker workaround: Remove check for KAFKA_ZOOKEEPER_CONNECT parameter 4 | sed -i '/KAFKA_ZOOKEEPER_CONNECT/d' /etc/confluent/docker/configure 5 | 6 | # Docker workaround: Ignore cub zk-ready 7 | sed -i 's/cub zk-ready/echo ignore zk-ready/' /etc/confluent/docker/ensure 8 | 9 | # KRaft required step: Format the storage directory with a new cluster ID 10 | echo "kafka-storage format --ignore-formatted --cluster-id=$(kafka-storage random-uuid) -c /etc/kafka/kafka.properties" >> /etc/confluent/docker/ensure 11 | -------------------------------------------------------------------------------- /mlflow/README.md: -------------------------------------------------------------------------------- 1 | # MLflow 2 | 3 | This folder contains the Dockerfile to build MLflow server image. MLflow could be deployed together with [Merlin's Helm Chart](https://github.com/caraml-dev/helm-charts/tree/main/charts/merlin). 4 | -------------------------------------------------------------------------------- /openapi-api-codegen.yaml: -------------------------------------------------------------------------------- 1 | packageName: client 2 | enumClassPrefix: true 3 | useOneOfDiscriminatorLookup: true 4 | # Global Properties 5 | globalProperties: 6 | apiTests: false 7 | modelTests: false 8 | apiDocs: false 9 | modelDocs: false 10 | -------------------------------------------------------------------------------- /openapi-sdk-codegen.yaml: -------------------------------------------------------------------------------- 1 | projectName: merlin-sdk 2 | packageName: client 3 | generateSourceCodeOnly: true 4 | useOneOfDiscriminatorLookup: true 5 | # Global Properties 6 | globalProperties: 7 | apiTests: false 8 | modelTests: false 9 | apiDocs: false 10 | modelDocs: false -------------------------------------------------------------------------------- /protos/merlin/transformer/spec/json.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package merlin.transformer; 4 | 5 | import "transformer/spec/common.proto"; 6 | 7 | option go_package = "github.com/caraml-dev/merlin/pkg/transformer/spec"; 8 | 9 | message JsonOutput { 10 | JsonTemplate jsonTemplate = 1; 11 | } 12 | 13 | message JsonTemplate { 14 | BaseJson baseJson = 1; 15 | repeated Field fields = 2; 16 | } 17 | 18 | message BaseJson { 19 | string jsonPath = 1; 20 | } 21 | 22 | message Field { 23 | string fieldName = 1; 24 | repeated Field fields = 2; 25 | oneof value { 26 | FromJson fromJson = 3; 27 | FromTable fromTable = 4; 28 | string expression = 5; 29 | } 30 | } 31 | 32 | enum PayloadType { 33 | INVALID = 0; 34 | RAW_REQUEST = 1; 35 | MODEL_RESPONSE = 2; 36 | } -------------------------------------------------------------------------------- /protos/merlin/transformer/spec/prediction_log.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package merlin.transformer; 4 | 5 | option go_package = "github.com/caraml-dev/merlin/pkg/transformer/spec"; 6 | 7 | // PredictionLogConfig contains information about prediction log 8 | message PredictionLogConfig { 9 | // flag to enable the prediction log 10 | bool enable = 1; 11 | // name of table that will be used to populate `rawFeaturesTable` field in prediction log 12 | string rawFeaturesTable = 2; 13 | // name of table that will be used to populate `entitiesTable` field in prediction log 14 | string entitiesTable = 3; 15 | } -------------------------------------------------------------------------------- /protos/merlin/transformer/spec/scaler.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package merlin.transformer; 4 | 5 | option go_package = "github.com/caraml-dev/merlin/pkg/transformer/spec"; 6 | 7 | message StandardScalerConfig { 8 | double mean = 1; 9 | double std = 2; 10 | } 11 | 12 | message MinMaxScalerConfig { 13 | double min = 1; 14 | double max = 2; 15 | } -------------------------------------------------------------------------------- /protos/merlin/transformer/spec/upi_autoload.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package merlin.transformer; 4 | 5 | option go_package = "github.com/caraml-dev/merlin/pkg/transformer/spec"; 6 | 7 | // UPIAutoload responsible to load given table names or variable names from request/response to standard transformer 8 | message UPIAutoload { 9 | // list of table names that explicitly declared on request/response 10 | repeated string tableNames = 1; 11 | // list of variable names that explicitly declared on request/response 12 | repeated string variableNames = 2; 13 | } 14 | 15 | -------------------------------------------------------------------------------- /protos/merlin/transformer/spec/variable.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package merlin.transformer; 4 | 5 | option go_package = "github.com/caraml-dev/merlin/pkg/transformer/spec"; 6 | 7 | import "transformer/spec/common.proto"; 8 | 9 | message Variable { 10 | string name = 1 ; 11 | oneof value { 12 | Literal literal = 2; 13 | string expression = 3; 14 | string jsonPath = 4; 15 | FromJson jsonPathConfig = 5; 16 | } 17 | } 18 | 19 | message Literal { 20 | oneof literal_value { 21 | string stringValue = 1; 22 | int64 intValue = 2; 23 | double floatValue = 3; 24 | bool boolValue = 4; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /python/.dockerignore: -------------------------------------------------------------------------------- 1 | # Git 2 | **/.gitignore 3 | 4 | # Docker 5 | **/docker-compose.yml 6 | **/Dockerfile 7 | **/.dockerignore 8 | 9 | # Byte-compiled / optimized / DLL files 10 | **/__pycache__/ 11 | **/*.py[cod] 12 | 13 | # C extensions 14 | **/*.so 15 | 16 | # Distribution / packaging 17 | **/env/ 18 | **/build/ 19 | **/develop-eggs/ 20 | **/dist/ 21 | **/eggs/ 22 | **/sdist/ 23 | **/*.egg-info/ 24 | **/*.egg 25 | 26 | # Test cache 27 | **/.pytest_cache 28 | 29 | # Virtual environment 30 | **/.env 31 | **/.venv/ 32 | **/venv/ 33 | 34 | # PyCharm 35 | **/.idea 36 | 37 | # Vim swap files 38 | **/*.swp 39 | 40 | # VS Code 41 | **/.vscode/ -------------------------------------------------------------------------------- /python/Makefile: -------------------------------------------------------------------------------- 1 | OBSERVATION_PUBLISHER_IMAGE_TAG ?= observation-publisher:dev 2 | 3 | .PHONY: observation-publisher 4 | observation-publisher: 5 | @echo "Building image for observation publisher..." 6 | @docker build -t ${OBSERVATION_PUBLISHER_IMAGE_TAG} -f observation-publisher/Dockerfile --progress plain . 7 | -------------------------------------------------------------------------------- /python/batch-predictor/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | name = "pypi" 3 | url = "https://pypi.org/simple" 4 | verify_ssl = true 5 | 6 | [dev-packages] 7 | merlin-batch-predictor = {editable = true,extras = ["test"],path = "."} 8 | 9 | [packages] 10 | merlin-batch-predictor = {extras = ["test"], file = ".", editable = true} 11 | -------------------------------------------------------------------------------- /python/batch-predictor/docker/main.py: -------------------------------------------------------------------------------- 1 | from merlinpyspark.__main__ import main 2 | 3 | main() 4 | -------------------------------------------------------------------------------- /python/batch-predictor/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/caraml-dev/merlin-pyspark-app 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/golang/protobuf v1.4.2 7 | github.com/grpc-ecosystem/grpc-gateway v1.14.6 // indirect 8 | github.com/kr/pretty v0.1.0 // indirect 9 | github.com/mitchellh/protoc-gen-go-json v1.1.0 // indirect 10 | google.golang.org/protobuf v1.25.0 11 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect 12 | ) 13 | -------------------------------------------------------------------------------- /python/batch-predictor/integration_test/merlin/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *~ 18 | # Various IDEs 19 | .project 20 | .idea/ 21 | *.tmproj 22 | -------------------------------------------------------------------------------- /python/batch-predictor/integration_test/merlin/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | appVersion: "1.0" 3 | description: A Helm chart for Kubernetes 4 | name: merlin 5 | version: 0.1.0 6 | -------------------------------------------------------------------------------- /python/batch-predictor/integration_test/merlin/templates/jobspec-configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: {{ template "merlin.fullname" . }}-jobspec 5 | labels: 6 | app: {{ template "merlin.name" . }} 7 | chart: {{ template "merlin.chart" . }} 8 | release: {{ .Release.Name | quote }} 9 | heritage: {{ .Release.Service | quote }} 10 | data: 11 | jobspec.yaml: |- 12 | {{.Values.jobSpec | toYaml | indent 4}} -------------------------------------------------------------------------------- /python/batch-predictor/integration_test/merlin/templates/serviceaccount-secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: {{ template "merlin.fullname" . }}-secret 5 | labels: 6 | app: {{ template "merlin.name" . }} 7 | chart: {{ template "merlin.chart" . }} 8 | release: {{ .Release.Name | quote }} 9 | heritage: {{ .Release.Service | quote }} 10 | type: Opaque 11 | data: 12 | service-account.json: {{ .Values.serviceAccount }} -------------------------------------------------------------------------------- /python/batch-predictor/integration_test/scripts/init-credentials.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o errexit 4 | set -o nounset 5 | set -o pipefail 6 | 7 | CLUSTER="$1" 8 | 9 | echo "Initializing credentials" 10 | kubectl config set-credentials "${CLUSTER}" --username="$(vault read -field=username secret/${CLUSTER})" --password="$(vault read -field=password secret/${CLUSTER})" 11 | echo "$(vault read -field=certs secret/${CLUSTER})" > ./certs 12 | kubectl config set-cluster "${CLUSTER}" --server="https://$(vault read -field=master_ip secret/${CLUSTER})" --certificate-authority=./certs 13 | kubectl config set-context "${CLUSTER}" --cluster="${CLUSTER}" --user="${CLUSTER}" 14 | kubectl config use-context "${CLUSTER}" 15 | echo "Credential initialized for cluster: ${CLUSTER}" -------------------------------------------------------------------------------- /python/batch-predictor/integration_test/scripts/tear-down.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o errexit 4 | set -o nounset 5 | set -o pipefail 6 | 7 | CI_COMMIT_SHORT_SHA="$1" 8 | 9 | helm delete --purge test-spark-e2e-${CI_COMMIT_SHORT_SHA} 10 | bq rm -f --project_id project dsp.merlin_iris_result_${CI_COMMIT_SHORT_SHA} -------------------------------------------------------------------------------- /python/batch-predictor/merlinpyspark/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 The Merlin Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | -------------------------------------------------------------------------------- /python/batch-predictor/merlinpyspark/spec/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/batch-predictor/merlinpyspark/spec/__init__.py -------------------------------------------------------------------------------- /python/batch-predictor/merlinpyspark/version.py: -------------------------------------------------------------------------------- 1 | VERSION = "0.0.0" 2 | -------------------------------------------------------------------------------- /python/batch-predictor/requirements.txt: -------------------------------------------------------------------------------- 1 | cloudpickle==2.0.0 2 | findspark==2.0.1 3 | merlin-sdk==0.0.0 4 | mlflow==1.26.1 5 | pyarrow>=0.14.1,<=17.0.0 6 | pyspark==3.1.3 7 | setuptools<75 -------------------------------------------------------------------------------- /python/batch-predictor/requirements_test.txt: -------------------------------------------------------------------------------- 1 | pytest 2 | pytest-cov 3 | mypy 4 | google-cloud-bigquery 5 | scikit-learn>=1.1.2 6 | joblib>=0.13.0,<1.2.0 # >=1.2.0 upon upgrade of kserve's version 7 | mypy-protobuf>=1.19 8 | types-PyYAML -------------------------------------------------------------------------------- /python/batch-predictor/sample/sample_1.yaml: -------------------------------------------------------------------------------- 1 | kind: PredictionJob 2 | version: v1 3 | name: integration-test 4 | bigquerySource: 5 | table: "project.dataset.table_iris" 6 | features: 7 | - sepal_length 8 | - sepal_width 9 | - petal_length 10 | - petal_width 11 | model: 12 | type: PYFUNC_V2 13 | uri: gs://bucket-name/e2e/artifacts/model 14 | result: 15 | type: DOUBLE 16 | bigquerySink: 17 | table: "project.dataset.table_iris_result" 18 | result_column: "prediction" 19 | save_mode: OVERWRITE 20 | options: 21 | project: "project" 22 | temporaryGcsBucket: "bucket-name" 23 | -------------------------------------------------------------------------------- /python/batch-predictor/test-config/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/batch-predictor/test-config/.keep -------------------------------------------------------------------------------- /python/batch-predictor/test-model/MLmodel: -------------------------------------------------------------------------------- 1 | artifact_path: model 2 | flavors: 3 | python_function: 4 | artifacts: 5 | model_path: 6 | path: artifacts/model.joblib 7 | uri: model/model.joblib 8 | cloudpickle_version: 2.0.0 9 | env: conda.yaml 10 | loader_module: mlflow.pyfunc.model 11 | python_model: python_model.pkl 12 | python_version: 3.9.22 13 | mlflow_version: 1.26.1 14 | model_uuid: 49ff290d6d854ee3ad83f6233ad891b9 15 | run_id: d65892b5f2f44cbaae64c682c61bccd6 16 | utc_time_created: '2024-03-18 09:59:03.832273' 17 | -------------------------------------------------------------------------------- /python/batch-predictor/test-model/artifacts/model.joblib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/batch-predictor/test-model/artifacts/model.joblib -------------------------------------------------------------------------------- /python/batch-predictor/test-model/conda.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - python=3.9.* 3 | - pip: 4 | - mlflow 5 | - joblib>=0.13.0,<1.2.0 6 | - numpy>=1.19.5,<1.23.4 7 | - pandas==1.3.5 8 | - pytest 9 | - pytest-xdist 10 | - scikit-learn>=1.1.2 11 | -------------------------------------------------------------------------------- /python/batch-predictor/test-model/python_env.yaml: -------------------------------------------------------------------------------- 1 | python: 3.9.22 2 | build_dependencies: 3 | - pip==24.0 4 | - setuptools==69.0.3 5 | - wheel==0.42.0 6 | dependencies: 7 | - -r requirements.txt 8 | -------------------------------------------------------------------------------- /python/batch-predictor/test-model/python_model.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/batch-predictor/test-model/python_model.pkl -------------------------------------------------------------------------------- /python/batch-predictor/test-model/requirements.txt: -------------------------------------------------------------------------------- 1 | mlflow 2 | scikit-learn>=1.1.2 3 | joblib>=0.13.0,<1.2.0 4 | numpy>=1.19.5,<1.23.4 5 | pandas==1.3.5 6 | pytest 7 | pytest-xdist -------------------------------------------------------------------------------- /python/batch-predictor/test/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 The Merlin Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import os 16 | 17 | os.makedirs('test-config', exist_ok=True) -------------------------------------------------------------------------------- /python/batch-predictor/test/util/test_utils.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 The Merlin Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | def write_config(path, config_yaml): 17 | with open(path, "w") as f: 18 | f.write(config_yaml) 19 | return path -------------------------------------------------------------------------------- /python/observation-publisher/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.10 2 | 3 | WORKDIR /mlobs 4 | COPY sdk ./sdk 5 | WORKDIR /mlobs/observation-publisher 6 | COPY observation-publisher/requirements.txt . 7 | RUN pip install -r requirements.txt 8 | RUN rm requirements.txt 9 | WORKDIR /mlobs 10 | COPY observation-publisher ./observation-publisher 11 | WORKDIR /mlobs/observation-publisher 12 | ENV PYTHONUNBUFFERED 1 13 | ENTRYPOINT ["python", "-m", "publisher"] -------------------------------------------------------------------------------- /python/observation-publisher/Makefile: -------------------------------------------------------------------------------- 1 | ENVIRONMENT_CONFIG = "example-override" 2 | 3 | .PHONY: setup 4 | setup: 5 | @echo "Setting up environment..." 6 | @pip install -r requirements.txt --use-pep517 7 | @pip install -r requirements-dev.txt 8 | 9 | .PHONY: pip-compile 10 | pip-compile: 11 | @echo "Compiling requirements..." 12 | @python -m piptools compile 13 | 14 | .PHONY: test 15 | test: 16 | @echo "Running tests..." 17 | @python -m pytest -m "not integration" 18 | 19 | .PHONY: test-integration 20 | integration-test: 21 | @echo "Running integration tests..." 22 | @python -m pytest -m "integration" 23 | 24 | .PHONY: run 25 | run: 26 | @echo "Running observation publisher..." 27 | @python -m publisher +environment=${ENVIRONMENT_CONFIG} 28 | -------------------------------------------------------------------------------- /python/observation-publisher/conf/config.yaml: -------------------------------------------------------------------------------- 1 | defaults: 2 | - base_config 3 | - _self_ 4 | - override hydra/hydra_logging: disabled 5 | - override hydra/job_logging: disabled 6 | 7 | hydra: 8 | output_subdir: null 9 | run: 10 | dir: . 11 | 12 | environment: {} 13 | -------------------------------------------------------------------------------- /python/observation-publisher/publisher/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/observation-publisher/publisher/__init__.py -------------------------------------------------------------------------------- /python/observation-publisher/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.pytest.ini_options] 2 | addopts = [ 3 | "--import-mode=importlib", 4 | ] 5 | markers = [ 6 | "integration: mark a test as integration test" 7 | ] 8 | 9 | [tool.mypy] 10 | exclude = "test.*" 11 | 12 | [[tool.mypy.overrides]] 13 | module = ["arize.*", "merlin.*", "confluent_kafka.*", "caraml.upi.*", "pyarrow.*"] 14 | ignore_missing_imports = true 15 | -------------------------------------------------------------------------------- /python/observation-publisher/requirements-dev.txt: -------------------------------------------------------------------------------- 1 | pip-tools==7.3.0 2 | pytest==7.4.3 3 | types-requests==2.31.0.20231231 4 | types-PyYAML==6.0.12.12 5 | types-jmespath==1.0.2.7 6 | mypy==1.7.1 7 | mypy-extensions==1.0.0 8 | db-dtypes==1.2.0 9 | pyodps==0.12.2 -------------------------------------------------------------------------------- /python/observation-publisher/requirements.in: -------------------------------------------------------------------------------- 1 | confluent-kafka>=2.3.0 2 | caraml-upi-protos>=1.0.0 3 | arize>=7.7.0 4 | hydra-core>=1.3.0 5 | pandas>=1.0.0 6 | google-cloud-bigquery 7 | prometheus-client >= 0.19.0 8 | typing-extensions==4.9.0 9 | pyodps==0.12.2 10 | -e file:../sdk -------------------------------------------------------------------------------- /python/observation-publisher/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/observation-publisher/tests/__init__.py -------------------------------------------------------------------------------- /python/observation-publisher/tests/common_fixtures.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import pytest 4 | 5 | 6 | @pytest.fixture 7 | def bq_project() -> str: 8 | return os.environ.get("INTEGRATION_TEST_BQ_PROJECT") 9 | 10 | 11 | @pytest.fixture 12 | def bq_dataset() -> str: 13 | return os.environ.get("INTEGRATION_TEST_BQ_DATASET") 14 | -------------------------------------------------------------------------------- /python/pyfunc-scaffolding/cookiecutter.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_name": "Sample", 3 | "project_slug": "{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}", 4 | "model_name": "pyfunc-sample", 5 | "model_slug": "{{ cookiecutter.model_name.lower().replace(' ', '_').replace('-', '_') }}", 6 | "merlin_sdk_version": "0.3.0", 7 | "environment_name": "id" 8 | } -------------------------------------------------------------------------------- /python/pyfunc-scaffolding/{{cookiecutter.model_slug}}/.env.sample: -------------------------------------------------------------------------------- 1 | MODEL_NAME={{ cookiecutter.model_name }} 2 | MODEL_PATH="artifact/" 3 | 4 | GOOGLE_CLOUD_PROJECT=**YOUR GCP PROJECT** 5 | MERLIN_URL="http://merlin.dev/api/merlin" 6 | MERLIN_PROJECT="{{ cookiecutter.project_name }}" 7 | MERLIN_ENV_NAME="{{ cookiecutter.environment_name }}-staging" 8 | -------------------------------------------------------------------------------- /python/pyfunc-scaffolding/{{cookiecutter.model_slug}}/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | name = "pypi" 3 | url = "https://pypi.org/simple" 4 | verify_ssl = true 5 | 6 | [packages] 7 | merlin-sdk = "=={{ cookiecutter.merlin_sdk_version }}" 8 | 9 | [requires] 10 | python_version = "3.9" 11 | -------------------------------------------------------------------------------- /python/pyfunc-scaffolding/{{cookiecutter.model_slug}}/env/conda.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - python=3.9 3 | -------------------------------------------------------------------------------- /python/pyfunc-scaffolding/{{cookiecutter.model_slug}}/requirements.txt: -------------------------------------------------------------------------------- 1 | merlin-sdk=={{ cookiecutter.merlin_sdk_version }} -------------------------------------------------------------------------------- /python/pyfunc-scaffolding/{{cookiecutter.model_slug}}/src/constant.py: -------------------------------------------------------------------------------- 1 | ARTIFACT_MODEL_PATH = "model_path" -------------------------------------------------------------------------------- /python/pyfunc-scaffolding/{{cookiecutter.model_slug}}/src/util/prometheus.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | def safe_prometheus_name(name): 4 | return re.sub('[^0-9a-zA-Z_]+', '_', name) -------------------------------------------------------------------------------- /python/pyfunc-server/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: setup 2 | setup: 3 | pip install pipenv 4 | $(MAKE) build_install_sdk 5 | pipenv install --skip-lock -e .[test] 6 | 7 | .PHONY: test 8 | test: type_check 9 | pipenv run pytest -W ignore -s 10 | 11 | .PHONY: type_check 12 | type_check: 13 | pipenv run mypy --ignore-missing-imports pyfuncserver 14 | 15 | .PHONY: benchmark 16 | benchmark: 17 | cd benchmark && ./benchmark.sh 18 | 19 | .PHONY: build_install_sdk 20 | build_install_sdk: 21 | cd ../sdk && pip install setuptools setuptools_scm twine wheel 22 | cd ../sdk && python setup.py sdist bdist_wheel 23 | pipenv run pip install ../sdk/dist/merlin_sdk-0.0.0-py3-none-any.whl -------------------------------------------------------------------------------- /python/pyfunc-server/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | name = "pypi" 3 | url = "https://pypi.org/simple" 4 | verify_ssl = true 5 | 6 | [packages] 7 | pyfuncserver = {editable = true, extras = ["test"], path = "."} 8 | merlin-pyfunc-server = {extras = ["test"], file = ".", editable = true} 9 | 10 | [dev-packages] 11 | -------------------------------------------------------------------------------- /python/pyfunc-server/benchmark/large.cfg: -------------------------------------------------------------------------------- 1 | POST http://localhost:8080/v1/models/model:predict 2 | @benchmark/large.json -------------------------------------------------------------------------------- /python/pyfunc-server/benchmark/medium.cfg: -------------------------------------------------------------------------------- 1 | POST http://localhost:8080/v1/models/model:predict 2 | @benchmark/medium.json -------------------------------------------------------------------------------- /python/pyfunc-server/benchmark/small.cfg: -------------------------------------------------------------------------------- 1 | POST http://localhost:8080/v1/models/model:predict 2 | @benchmark/small.json -------------------------------------------------------------------------------- /python/pyfunc-server/benchmark/small.json: -------------------------------------------------------------------------------- 1 | { 2 | "mid_pickup_eta": 0, 3 | "order_driver_cut": 1000, 4 | "order_gmv": 10000, 5 | "order_surge_factor": 1 6 | } -------------------------------------------------------------------------------- /python/pyfunc-server/docker/local.Dockerfile: -------------------------------------------------------------------------------- 1 | ARG BASE_IMAGE 2 | FROM ${BASE_IMAGE} 3 | 4 | # Download and install user model dependencies 5 | ARG MODEL_DEPENDENCIES_URL 6 | COPY ${MODEL_DEPENDENCIES_URL} conda.yaml 7 | 8 | ARG MERLIN_DEP_CONSTRAINT 9 | RUN process_conda_env.sh conda.yaml "merlin-pyfunc-server" "${MERLIN_DEP_CONSTRAINT}" 10 | RUN conda env create --name merlin-model --file conda.yaml 11 | 12 | # Download and dry-run user model artifacts and code 13 | ARG MODEL_ARTIFACTS_URL 14 | COPY ${MODEL_ARTIFACTS_URL} model 15 | RUN /bin/bash -c ". activate merlin-model && merlin-pyfunc-server --model_dir model --dry_run" 16 | 17 | CMD ["run.sh"] 18 | -------------------------------------------------------------------------------- /python/pyfunc-server/examples/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/pyfunc-server/examples/__init__.py -------------------------------------------------------------------------------- /python/pyfunc-server/examples/echo_http/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/pyfunc-server/examples/echo_http/__init__.py -------------------------------------------------------------------------------- /python/pyfunc-server/examples/echo_http/echo_http.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | import merlin 4 | from merlin.model import PyFuncModel 5 | 6 | 7 | class EchoModel(PyFuncModel): 8 | def initialize(self, artifacts): 9 | pass 10 | 11 | def infer(self, request): 12 | logging.info("request: %s", request) 13 | return request 14 | 15 | 16 | if __name__ == "__main__": 17 | # Run pyfunc model locally without uploading to Merlin server 18 | merlin.run_pyfunc_model( 19 | model_instance=EchoModel(), 20 | conda_env="env.yaml", 21 | ) 22 | -------------------------------------------------------------------------------- /python/pyfunc-server/examples/echo_http/env.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - python=3.10 3 | -------------------------------------------------------------------------------- /python/pyfunc-server/examples/echo_upi/README.md: -------------------------------------------------------------------------------- 1 | # Echo UPI Model Examples 2 | 3 | Run the server locally: 4 | 5 | ``` 6 | python upi_server.py 7 | ``` 8 | 9 | In different terminal session, run the client: 10 | 11 | ``` 12 | python upi_client.py 13 | ``` 14 | -------------------------------------------------------------------------------- /python/pyfunc-server/examples/echo_upi/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/pyfunc-server/examples/echo_upi/__init__.py -------------------------------------------------------------------------------- /python/pyfunc-server/examples/echo_upi/env.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - python=3.10 3 | -------------------------------------------------------------------------------- /python/pyfunc-server/examples/iris_http/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/pyfunc-server/examples/iris_http/__init__.py -------------------------------------------------------------------------------- /python/pyfunc-server/examples/iris_http/env.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - python=3.10.* 3 | - pip: 4 | - joblib>=0.13.0,<1.2.0 # >=1.2.0 upon upgrade of kserve's version 5 | - numpy<=1.23.5 # Temporary pin numpy due to https://numpy.org/doc/stable/release/1.20.0-notes.html#numpy-1-20-0-release-notes 6 | - scikit-learn>=1.1.2 7 | - xgboost==1.6.2 8 | -------------------------------------------------------------------------------- /python/pyfunc-server/examples/iris_http/models/model_1.bst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/pyfunc-server/examples/iris_http/models/model_1.bst -------------------------------------------------------------------------------- /python/pyfunc-server/examples/iris_http/models/model_2.joblib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/pyfunc-server/examples/iris_http/models/model_2.joblib -------------------------------------------------------------------------------- /python/pyfunc-server/prometheus/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/pyfunc-server/prometheus/.keep -------------------------------------------------------------------------------- /python/pyfunc-server/pyfuncserver/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 The Merlin Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /python/pyfunc-server/pyfuncserver/metrics/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/pyfunc-server/pyfuncserver/metrics/__init__.py -------------------------------------------------------------------------------- /python/pyfunc-server/pyfuncserver/model/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/pyfunc-server/pyfuncserver/model/__init__.py -------------------------------------------------------------------------------- /python/pyfunc-server/pyfuncserver/protocol/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/pyfunc-server/pyfuncserver/protocol/__init__.py -------------------------------------------------------------------------------- /python/pyfunc-server/pyfuncserver/protocol/rest/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/pyfunc-server/pyfuncserver/protocol/rest/__init__.py -------------------------------------------------------------------------------- /python/pyfunc-server/pyfuncserver/protocol/upi/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/pyfunc-server/pyfuncserver/protocol/upi/__init__.py -------------------------------------------------------------------------------- /python/pyfunc-server/pyfuncserver/publisher/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/pyfunc-server/pyfuncserver/publisher/__init__.py -------------------------------------------------------------------------------- /python/pyfunc-server/pyfuncserver/publisher/publisher.py: -------------------------------------------------------------------------------- 1 | from pyfuncserver.sampler.sampler import Sampler 2 | from merlin.pyfunc import PyFuncOutput 3 | from abc import ABC, abstractmethod 4 | import asyncio 5 | 6 | class Producer(ABC): 7 | 8 | @abstractmethod 9 | def produce(self, data: PyFuncOutput): 10 | pass 11 | 12 | class Publisher: 13 | def __init__(self, producer: Producer, sampler: Sampler) -> None: 14 | self.producer = producer 15 | self.sampler = sampler 16 | 17 | async def publish(self, output: PyFuncOutput): 18 | if not self.sampler.should_sample(): 19 | return 20 | 21 | self.producer.produce(output) -------------------------------------------------------------------------------- /python/pyfunc-server/pyfuncserver/sampler/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/pyfunc-server/pyfuncserver/sampler/__init__.py -------------------------------------------------------------------------------- /python/pyfunc-server/pyfuncserver/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/pyfunc-server/pyfuncserver/utils/__init__.py -------------------------------------------------------------------------------- /python/pyfunc-server/pyfuncserver/version.py: -------------------------------------------------------------------------------- 1 | VERSION = "0.0.0" 2 | -------------------------------------------------------------------------------- /python/pyfunc-server/requirements.txt: -------------------------------------------------------------------------------- 1 | argparse>=1.4.0 2 | caraml-upi-protos>=0.3.4 3 | cloudpickle==2.0.0 4 | confluent-kafka==2.3.0 5 | grpcio-health-checking 6 | grpcio-reflection 7 | merlin-sdk==0.0.0 8 | numpy>=1.8.2 9 | orjson>=2.6.8 10 | prometheus-client 11 | setuptools<75 12 | tornado 13 | uvloop 14 | -------------------------------------------------------------------------------- /python/pyfunc-server/test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/pyfunc-server/test/__init__.py -------------------------------------------------------------------------------- /python/pyfunc-server/test/local-artifacts/MLmodel: -------------------------------------------------------------------------------- 1 | artifact_path: model 2 | flavors: 3 | python_function: 4 | artifacts: 5 | sklearn_model: 6 | path: artifacts/model_2.joblib 7 | uri: models/model_2.joblib 8 | xgb_model: 9 | path: artifacts/model_1.bst 10 | uri: models/model_1.bst 11 | cloudpickle_version: 2.0.0 12 | env: conda.yaml 13 | loader_module: mlflow.pyfunc.model 14 | python_model: python_model.pkl 15 | python_version: 3.9.22 16 | mlflow_version: 1.26.1 17 | model_uuid: 126ccf3f97024b43a011b5aee072010c 18 | run_id: 74232b10e8064ccda29611c265921034 19 | utc_time_created: '2024-03-18 07:27:00.346244' 20 | -------------------------------------------------------------------------------- /python/pyfunc-server/test/local-artifacts/README.md: -------------------------------------------------------------------------------- 1 | This folder contains model artifacts used by `build-pyfunc-server-base-image` GitHub Actions job. 2 | -------------------------------------------------------------------------------- /python/pyfunc-server/test/local-artifacts/artifacts/model_1.bst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/pyfunc-server/test/local-artifacts/artifacts/model_1.bst -------------------------------------------------------------------------------- /python/pyfunc-server/test/local-artifacts/artifacts/model_2.joblib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/pyfunc-server/test/local-artifacts/artifacts/model_2.joblib -------------------------------------------------------------------------------- /python/pyfunc-server/test/local-artifacts/conda.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - python=3.9.* 3 | - pip: 4 | - mlflow 5 | - joblib>=0.13.0,<1.2.0 6 | - numpy<=1.23.5 7 | - scikit-learn>=1.1.2 8 | - xgboost==1.6.2 9 | -------------------------------------------------------------------------------- /python/pyfunc-server/test/local-artifacts/python_env.yaml: -------------------------------------------------------------------------------- 1 | python: 3.9.22 2 | build_dependencies: 3 | - pip==24.0 4 | - setuptools==69.0.3 5 | - wheel==0.42.0 6 | dependencies: 7 | - -r requirements.txt 8 | -------------------------------------------------------------------------------- /python/pyfunc-server/test/local-artifacts/python_model.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/pyfunc-server/test/local-artifacts/python_model.pkl -------------------------------------------------------------------------------- /python/pyfunc-server/test/local-artifacts/requirements.txt: -------------------------------------------------------------------------------- 1 | mlflow 2 | joblib>=0.13.0,<1.2.0 3 | numpy<=1.23.5 4 | scikit-learn>=1.1.2 5 | xgboost==1.6.2 -------------------------------------------------------------------------------- /python/pyfunc-server/test/publisher/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/pyfunc-server/test/publisher/__init__.py -------------------------------------------------------------------------------- /python/pyfunc-server/test/sampler/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/pyfunc-server/test/sampler/__init__.py -------------------------------------------------------------------------------- /python/pyfunc-server/test/sampler/test_sampler.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import math 3 | 4 | from pyfuncserver.sampler.sampler import RatioSampling 5 | 6 | @pytest.mark.parametrize("num_requests, ratio", [(3000, 0.2), (10000, 0.3), (5000, 0.5)]) 7 | def test_ratio_sampling(num_requests, ratio): 8 | ratio_based_sampler = RatioSampling(ratio=ratio) 9 | num_sampled = 0 10 | for _ in range(num_requests): 11 | if ratio_based_sampler.should_sample(): 12 | num_sampled = num_sampled + 1 13 | actual_ratio = num_sampled / num_requests 14 | print(f"actual ratio {actual_ratio} expected ratio {ratio}") 15 | assert math.isclose(a=ratio, b=actual_ratio, abs_tol=0.02) 16 | -------------------------------------------------------------------------------- /python/pyfunc-server/test/util/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/pyfunc-server/test/util/__init__.py -------------------------------------------------------------------------------- /python/pyfunc-server/test/utils.py: -------------------------------------------------------------------------------- 1 | import time 2 | import requests 3 | 4 | 5 | def wait_server_ready(url, timeout_second=10, tick_second=2): 6 | ellapsed_second = 0 7 | while (ellapsed_second < timeout_second): 8 | try: 9 | resp = requests.get(url) 10 | if resp.status_code == 200: 11 | return 12 | except Exception as e: 13 | print(f"{url} is not ready: {e}") 14 | 15 | time.sleep(tick_second) 16 | ellapsed_second += tick_second 17 | 18 | if ellapsed_second >= timeout_second: 19 | raise TimeoutError("server is not ready within specified timeout duration") 20 | -------------------------------------------------------------------------------- /python/sdk/.dockerignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | *.pyc 3 | *.pyo 4 | *.pyd 5 | .Python 6 | env 7 | venv 8 | pip-log.txt 9 | pip-delete-this-directory.txt 10 | .tox 11 | .coverage 12 | .coverage.* 13 | .cache 14 | nosetests.xml 15 | coverage.xml 16 | *.cover 17 | *.log 18 | .git 19 | .mypy_cache 20 | .pytest_cache 21 | .hypothesis 22 | -------------------------------------------------------------------------------- /python/sdk/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG PYTHON_VERSION=3.9 2 | FROM python:${PYTHON_VERSION}-slim-buster 3 | 4 | LABEL org.opencontainers.image.source https://github.com/caraml-dev/merlin 5 | 6 | WORKDIR /root 7 | ENV PYTHONPATH /root 8 | 9 | RUN apt-get update && apt-get install build-essential curl vim wget -y 10 | 11 | COPY . .${WORKDIR} 12 | 13 | RUN pip install . 14 | RUN pip install ".[test]" 15 | -------------------------------------------------------------------------------- /python/sdk/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | name = "pypi" 3 | url = "https://pypi.org/simple" 4 | verify_ssl = true 5 | 6 | [dev-packages] 7 | ipykernel = "*" 8 | jupyter = "*" 9 | pytest = "*" 10 | pytest-xdist = "*" 11 | 12 | [packages] 13 | merlin-sdk = {extras = ["test"],path = "."} 14 | cookiecutter = '==2.1.1' 15 | -------------------------------------------------------------------------------- /python/sdk/client/api_response.py: -------------------------------------------------------------------------------- 1 | """API response object.""" 2 | 3 | from __future__ import annotations 4 | from typing import Any, Dict, Optional, Generic, TypeVar 5 | from pydantic import Field, StrictInt, StrictStr, StrictBytes, BaseModel 6 | 7 | T = TypeVar("T") 8 | 9 | class ApiResponse(BaseModel, Generic[T]): 10 | """ 11 | API response object 12 | """ 13 | 14 | status_code: StrictInt = Field(description="HTTP status code") 15 | headers: Optional[Dict[StrictStr, StrictStr]] = Field(None, description="HTTP headers") 16 | data: T = Field(description="Deserialized data given the data type") 17 | raw_data: StrictBytes = Field(description="Raw data (HTTP response body)") 18 | 19 | model_config = { 20 | "arbitrary_types_allowed": True 21 | } 22 | -------------------------------------------------------------------------------- /python/sdk/docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /python/sdk/docs/README.md: -------------------------------------------------------------------------------- 1 | # Documentation 2 | 3 | ## Building locally 4 | 5 | Install Python requirements: 6 | ```bash 7 | pip install -r requirements_docs.txt 8 | ``` 9 | 10 | Install `pandoc` 11 | ```bash 12 | conda install pandoc=1.19.2 13 | ``` 14 | 15 | Generate doc 16 | ```bash 17 | make html 18 | ``` 19 | 20 | The output is located at `_build` directory 21 | 22 | Re-generating doc (delete all the files in the `_build` directory before generating the documentation again) 23 | ```bash 24 | make clean 25 | ``` 26 | 27 | ## Publishing 28 | 29 | The documentation for the latest **commit** is located [here](https://merlin-sdk.readthedocs.io/en/latest/). 30 | -------------------------------------------------------------------------------- /python/sdk/docs/index.rst: -------------------------------------------------------------------------------- 1 | Welcome to Merlin SDK documentation! 2 | ==================================== 3 | 4 | Make machine learning deployment magical. 5 | 6 | Content 7 | ======= 8 | 9 | .. toctree:: 10 | :maxdepth: 2 11 | 12 | Introduction 13 | Getting Started 14 | Sample Notebooks 15 | 16 | .. toctree:: 17 | :maxdepth: 1 18 | :caption: Reference 19 | 20 | Python API reference -------------------------------------------------------------------------------- /python/sdk/docs/requirements_docs.txt: -------------------------------------------------------------------------------- 1 | sphinx>=1.8.4 2 | sphinx-autobuild>=0.7.1 3 | sphinx-autodoc-typehints>=1.6.0 4 | sphinx-rtd-theme>=0.4.3 5 | m2r>=0.2.1 6 | sphinxcontrib-apidoc>=0.3.0 7 | sphinxcontrib-confluencebuilder>=1.1.0 8 | nbsphinx>=0.4.2 9 | nbsphinx-link>=1.2.0 10 | ipykernel>=5.1.0 11 | ipython>=7.2.0 12 | ipypublish 13 | certifi>=2017.4.17 14 | python-dateutil>=2.1 15 | six>=1.10 16 | mlflow>=1.2.0,<=1.23.0 17 | google-cloud-storage>=1.19.0 18 | urllib3>=1.23 19 | PyPrind>=2.11.2 20 | Click>=7.0 21 | cloudpickle==2.0.0 22 | cookiecutter>=1.7.0 -------------------------------------------------------------------------------- /python/sdk/docs/sample/batch_iris.nblink: -------------------------------------------------------------------------------- 1 | { 2 | "path": "../../../../examples/batch/Batch Prediction Tutorial 1 - Iris Classifier.ipynb" 3 | } -------------------------------------------------------------------------------- /python/sdk/docs/sample/batch_taxi.nblink: -------------------------------------------------------------------------------- 1 | { 2 | "path": "../../../../examples/batch/Batch Prediction Tutorial 2 - New York Taxi .ipynb" 3 | } -------------------------------------------------------------------------------- /python/sdk/docs/sample/metrics.nblink: -------------------------------------------------------------------------------- 1 | { 2 | "path": "../../../../examples/metrics/Metrics.ipynb" 3 | } -------------------------------------------------------------------------------- /python/sdk/docs/sample/notebooks.rst: -------------------------------------------------------------------------------- 1 | ========= 2 | Notebooks 3 | ========= 4 | 5 | .. toctree:: 6 | :maxdepth: 0 7 | 8 | Deploying SKLearn Model 9 | Deploying XGBoost Model 10 | Deploying Tensorflow Model 11 | Deploying PyTorch Model 12 | Deploying Python Function Model 13 | Custom Metrics 14 | Batch Prediction 15 | Predicting NY Taxi Fare Using Batch Prediction -------------------------------------------------------------------------------- /python/sdk/docs/sample/pyfunc.nblink: -------------------------------------------------------------------------------- 1 | { 2 | "path": "../../../../examples/pyfunc/Pyfunc.ipynb" 3 | } -------------------------------------------------------------------------------- /python/sdk/docs/sample/pytorch.nblink: -------------------------------------------------------------------------------- 1 | { 2 | "path": "../../../../examples/pytorch/Pytorch.ipynb" 3 | } -------------------------------------------------------------------------------- /python/sdk/docs/sample/sample_metric.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/sdk/docs/sample/sample_metric.png -------------------------------------------------------------------------------- /python/sdk/docs/sample/sklearn.nblink: -------------------------------------------------------------------------------- 1 | { 2 | "path": "../../../../examples/sklearn/SKLearn.ipynb" 3 | } -------------------------------------------------------------------------------- /python/sdk/docs/sample/tensorflow.nblink: -------------------------------------------------------------------------------- 1 | { 2 | "path": "../../../../examples/tensorflow/Tensorflow.ipynb" 3 | } -------------------------------------------------------------------------------- /python/sdk/docs/sample/xgboost.nblink: -------------------------------------------------------------------------------- 1 | { 2 | "path": "../../../../examples/xgboost/XGBoost.ipynb" 3 | } -------------------------------------------------------------------------------- /python/sdk/docs/serve.json: -------------------------------------------------------------------------------- 1 | { 2 | "cleanUrls": false, 3 | "rewrites": [ 4 | { "source": "/", "destination": "/index.html" } 5 | ] 6 | } -------------------------------------------------------------------------------- /python/sdk/docs/source/merlin.docker.rst: -------------------------------------------------------------------------------- 1 | merlin.docker package 2 | ===================== 3 | 4 | .. automodule:: merlin.docker 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | 9 | Submodules 10 | ---------- 11 | 12 | merlin.docker.docker module 13 | --------------------------- 14 | 15 | .. automodule:: merlin.docker.docker 16 | :members: 17 | :undoc-members: 18 | :show-inheritance: 19 | -------------------------------------------------------------------------------- /python/sdk/docs/source/modules.rst: -------------------------------------------------------------------------------- 1 | merlin 2 | ====== 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | merlin 8 | -------------------------------------------------------------------------------- /python/sdk/docs/sphinxcontrib/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This is free and unencumbered software released into the public domain. 3 | 4 | __import__('pkg_resources').declare_namespace(__name__) -------------------------------------------------------------------------------- /python/sdk/merlin/batch/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 The Merlin Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | -------------------------------------------------------------------------------- /python/sdk/merlin/deployment_mode.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class DeploymentMode(Enum): 5 | """ 6 | Deployment mode for deploying a model version 7 | """ 8 | SERVERLESS = "serverless" 9 | RAW_DEPLOYMENT = "raw_deployment" 10 | -------------------------------------------------------------------------------- /python/sdk/merlin/docker/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 The Merlin Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | -------------------------------------------------------------------------------- /python/sdk/merlin/docker/standard.Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2020 The Merlin Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ARG BASE_IMAGE 16 | ARG MODEL_PATH 17 | 18 | FROM ${BASE_IMAGE} 19 | 20 | COPY ${MODEL_PATH} /mnt/models -------------------------------------------------------------------------------- /python/sdk/merlin/observability/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/sdk/merlin/observability/__init__.py -------------------------------------------------------------------------------- /python/sdk/merlin/protocol.py: -------------------------------------------------------------------------------- 1 | import enum 2 | 3 | 4 | class Protocol(enum.Enum): 5 | """ 6 | Model deployment protocol. 7 | 8 | HTTP_JSON = Deploy model and expose HTTP server 9 | UPI_V1 = Deploy model and expose gRPC server compatible with universal-prediction-interface (https://github.com/caraml-dev/universal-prediction-interface) 10 | """ 11 | HTTP_JSON = "HTTP_JSON" 12 | UPI_V1 = "UPI_V1" 13 | -------------------------------------------------------------------------------- /python/sdk/merlin/version.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 The Merlin Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Placeholder. To be overriden with tag version by Github Actions pipeline. 16 | VERSION = "0.0.0" 17 | -------------------------------------------------------------------------------- /python/sdk/merlin_cli.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/sdk/merlin_cli.gif -------------------------------------------------------------------------------- /python/sdk/pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | addopts = -x -rA 3 | testpaths = test 4 | -------------------------------------------------------------------------------- /python/sdk/requirements.txt: -------------------------------------------------------------------------------- 1 | boto3>=1.35.39 2 | caraml-upi-protos>=0.3.1 3 | certifi>=2017.4.17 4 | Click>=7.0,<8.1.4 5 | cloudpickle==2.0.0 # used by mlflow 6 | cookiecutter>=1.7.2 7 | dataclasses-json>=0.5.2 # allow Flyte version 1.2.0 or above to import Merlin SDK 8 | docker<=6.1.3 9 | GitPython>=3.1.40 10 | google-cloud-storage>=1.19.0 11 | protobuf>=4.21.6,<5 12 | mlflow==1.26.1 13 | PyPrind>=2.11.2 14 | python_dateutil>=2.5.3 15 | PyYAML>=5.4 16 | six>=1.10 17 | urllib3>=1.26 18 | numpy>=1.26.4 19 | caraml-auth-google==0.0.0.post9 20 | pydantic==2.5.3 21 | grpcio-tools>=1.50.0,<1.63 22 | grpcio>=1.60.1 -------------------------------------------------------------------------------- /python/sdk/requirements_test.txt: -------------------------------------------------------------------------------- 1 | google-cloud-bigquery-storage>=0.7.0 2 | google-cloud-bigquery>=1.18.0 3 | joblib>=0.13.0,<1.2.0 # >=1.2.0 upon upgrade of kserve's version 4 | mypy>=0.812 5 | pytest-cov 6 | pytest-dependency 7 | pytest-xdist 8 | pytest 9 | recursive-diff>=1.0.0 10 | requests 11 | scikit-learn>=1.1.2 12 | types-python-dateutil 13 | types-PyYAML 14 | types-six 15 | types-protobuf 16 | urllib3-mock>=0.3.3 17 | xarray 18 | xgboost==1.6.2 -------------------------------------------------------------------------------- /python/sdk/test/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 The Merlin Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | -------------------------------------------------------------------------------- /python/sdk/test/batch/model/env.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - pip: 3 | - mlflow 4 | - scikit-learn>=1.1.2 5 | - joblib>=0.13.0,<1.2.0 # >=1.2.0 upon upgrade of kserve's version 6 | - numpy>=1.19.5,<1.23.4 # https://github.com/Azure/MachineLearningNotebooks/issues/1314 7 | - pandas==1.3.5 8 | - pytest 9 | - pytest-xdist 10 | -------------------------------------------------------------------------------- /python/sdk/test/custom-model/model.bst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/sdk/test/custom-model/model.bst -------------------------------------------------------------------------------- /python/sdk/test/feast_model.py: -------------------------------------------------------------------------------- 1 | from merlin.model import PyFuncModel 2 | 3 | 4 | class EchoModel(PyFuncModel): 5 | def infer(self, request: dict, **kwargs) -> dict: 6 | return request 7 | -------------------------------------------------------------------------------- /python/sdk/test/invalid-models/onnx-model-invalid/invalid.onnx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/sdk/test/invalid-models/onnx-model-invalid/invalid.onnx -------------------------------------------------------------------------------- /python/sdk/test/invalid-models/pyfunc-model-invalid/MLmodel: -------------------------------------------------------------------------------- 1 | flavors: 2 | python_function: 3 | cloudpickle_version: 2.0.0 4 | env: conda.yaml 5 | loader_module: mlflow.pyfunc.model 6 | python_model: python_model.pkl 7 | python_version: 3.8.5 8 | utc_time_created: '2020-01-20 09:53:55.355722' 9 | -------------------------------------------------------------------------------- /python/sdk/test/invalid-models/pyfunc-model-invalid/conda.yaml: -------------------------------------------------------------------------------- 1 | channels: 2 | - defaults 3 | dependencies: 4 | - python=3.8.5 5 | - pip: 6 | - mlflow 7 | - cloudpickle==2.0.0 8 | name: mlflow-env 9 | -------------------------------------------------------------------------------- /python/sdk/test/invalid-models/pyfunc-model-invalid/invalid_model.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/sdk/test/invalid-models/pyfunc-model-invalid/invalid_model.pkl -------------------------------------------------------------------------------- /python/sdk/test/invalid-models/pytorch-model-invalid/invalid.pt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/sdk/test/invalid-models/pytorch-model-invalid/invalid.pt -------------------------------------------------------------------------------- /python/sdk/test/invalid-models/sklearn-model-invalid/invalid.joblib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/sdk/test/invalid-models/sklearn-model-invalid/invalid.joblib -------------------------------------------------------------------------------- /python/sdk/test/invalid-models/tensorflow-model-invalid/1/invalid_model.pb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/sdk/test/invalid-models/tensorflow-model-invalid/1/invalid_model.pb -------------------------------------------------------------------------------- /python/sdk/test/invalid-models/tensorflow-model-invalid/1/variables/variables.data-00000-of-00002: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/sdk/test/invalid-models/tensorflow-model-invalid/1/variables/variables.data-00000-of-00002 -------------------------------------------------------------------------------- /python/sdk/test/invalid-models/tensorflow-model-invalid/1/variables/variables.data-00001-of-00002: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/sdk/test/invalid-models/tensorflow-model-invalid/1/variables/variables.data-00001-of-00002 -------------------------------------------------------------------------------- /python/sdk/test/invalid-models/tensorflow-model-invalid/1/variables/variables.index: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/sdk/test/invalid-models/tensorflow-model-invalid/1/variables/variables.index -------------------------------------------------------------------------------- /python/sdk/test/invalid-models/xgboost-model-invalid/invalid.bst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/sdk/test/invalid-models/xgboost-model-invalid/invalid.bst -------------------------------------------------------------------------------- /python/sdk/test/onnx-model/model.onnx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/sdk/test/onnx-model/model.onnx -------------------------------------------------------------------------------- /python/sdk/test/pyfunc/env.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - python=3.10.* 3 | - pip: 4 | - joblib>=0.13.0,<1.2.0 # >=1.2.0 upon upgrade of kserve's version 5 | - numpy<=1.23.5 # Temporary pin numpy due to https://numpy.org/doc/stable/release/1.20.0-notes.html#numpy-1-20-0-release-notes 6 | - scikit-learn>=1.1.2 7 | - xgboost==1.6.2 8 | - pytest 9 | - pytest-xdist==1.34.0 10 | -------------------------------------------------------------------------------- /python/sdk/test/pyfunc/model_1.bst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/sdk/test/pyfunc/model_1.bst -------------------------------------------------------------------------------- /python/sdk/test/pyfunc/model_2.joblib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/sdk/test/pyfunc/model_2.joblib -------------------------------------------------------------------------------- /python/sdk/test/pyfunc/table_data/normal.csv: -------------------------------------------------------------------------------- 1 | First Name,Last Name,Age,Weight,Is VIP 2 | Apple,Cider,25,48.8,TRUE 3 | Banana,Man,18,68,FALSE 4 | Zara,Vuitton,35,75,TRUE 5 | Sandra,Zawaska,32,55,FALSE 6 | Merlion,Krabby,23,57.22,FALSE -------------------------------------------------------------------------------- /python/sdk/test/pytorch-model/iris.pt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/sdk/test/pytorch-model/iris.pt -------------------------------------------------------------------------------- /python/sdk/test/pytorch-model/iris.py: -------------------------------------------------------------------------------- 1 | import torch.nn as nn 2 | import torch.nn.functional as F 3 | 4 | class IrisModel(nn.Module): 5 | # define nn 6 | def __init__(self): 7 | super(IrisModel, self).__init__() 8 | self.fc1 = nn.Linear(4, 100) 9 | self.fc2 = nn.Linear(100, 100) 10 | self.fc3 = nn.Linear(100, 3) 11 | self.softmax = nn.Softmax(dim=1) 12 | 13 | def forward(self, X): 14 | X = F.relu(self.fc1(X)) 15 | X = self.fc2(X) 16 | X = self.fc3(X) 17 | X = self.softmax(X) 18 | 19 | return X -------------------------------------------------------------------------------- /python/sdk/test/pytorch-model/iris_handler.py: -------------------------------------------------------------------------------- 1 | from ts.torch_handler.base_handler import BaseHandler 2 | 3 | class IrisHandler(BaseHandler): 4 | pass 5 | -------------------------------------------------------------------------------- /python/sdk/test/pytorch-model/pytorch-sample/config/config.properties: -------------------------------------------------------------------------------- 1 | inference_address=http://0.0.0.0:8085 2 | management_address=http://0.0.0.0:8085 3 | metrics_address=http://0.0.0.0:8082 4 | grpc_inference_port=7070 5 | grpc_management_port=7071 6 | enable_metrics_api=true 7 | metrics_format=prometheus 8 | number_of_netty_threads=4 9 | job_queue_size=10 10 | enable_envvars_config=true 11 | install_py_dep_per_model=true 12 | model_store=/mnt/models/model-store 13 | model_snapshot={"name":"startup.cfg","modelCount":1,"models":{"pytorch-sample-1-r1":{"1.0":{"defaultVersion":true,"marName":"pytorch-sample.mar","minWorkers":1,"maxWorkers":5,"batchSize":1,"maxBatchDelay":10,"responseTimeout":120}}}} 14 | -------------------------------------------------------------------------------- /python/sdk/test/pytorch-model/pytorch-sample/model-store/pytorch-sample.mar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/sdk/test/pytorch-model/pytorch-sample/model-store/pytorch-sample.mar -------------------------------------------------------------------------------- /python/sdk/test/requirements/empty_in.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/sdk/test/requirements/empty_in.yaml -------------------------------------------------------------------------------- /python/sdk/test/requirements/empty_out.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - python=3.10.* 3 | - pip: 4 | - merlin-pyfunc-server<0.42.0 5 | -------------------------------------------------------------------------------- /python/sdk/test/requirements/no_pip_reqs_in.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - python=3.7.* 3 | -------------------------------------------------------------------------------- /python/sdk/test/requirements/no_pip_reqs_out.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - python=3.10.* 3 | - pip: 4 | - merlin-pyfunc-server<0.42.0 5 | -------------------------------------------------------------------------------- /python/sdk/test/requirements/non_package_reqs_in.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - python=3.10.* 3 | - pip: 4 | - --extra-index-url=http://artifactory.com 5 | - --trusted-host=artifactory.com 6 | - scikit-learn==1.4.0 7 | -------------------------------------------------------------------------------- /python/sdk/test/requirements/non_package_reqs_out.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - python=3.10.* 3 | - pip: 4 | - merlin-pyfunc-server<0.42.0 5 | - --extra-index-url=http://artifactory.com 6 | - --trusted-host=artifactory.com 7 | - scikit-learn==1.4.0 8 | -------------------------------------------------------------------------------- /python/sdk/test/requirements/other_reqs_in.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - python=3.10.* 3 | - pip: 4 | - cloudpickle==2.0.0 5 | - scikit-learn==1.4.0 6 | - tensorflow==2.16.1 7 | -------------------------------------------------------------------------------- /python/sdk/test/requirements/other_reqs_out.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - python=3.10.* 3 | - pip: 4 | - merlin-pyfunc-server<0.42.0 5 | - cloudpickle==2.0.0 6 | - scikit-learn==1.4.0 7 | - tensorflow==2.16.1 8 | -------------------------------------------------------------------------------- /python/sdk/test/requirements/pyfunc_server_with_version_in.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - python=3.10.* 3 | - pip: 4 | - merlin-pyfunc-server==0.50.0 5 | -------------------------------------------------------------------------------- /python/sdk/test/requirements/pyfunc_server_with_version_out.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - python=3.10.* 3 | - pip: 4 | - merlin-pyfunc-server==0.50.0 5 | -------------------------------------------------------------------------------- /python/sdk/test/requirements/pyfunc_server_without_version_in.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - python=3.10.* 3 | - pip: 4 | - merlin-pyfunc-server 5 | -------------------------------------------------------------------------------- /python/sdk/test/requirements/pyfunc_server_without_version_out.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - python=3.10.* 3 | - pip: 4 | - merlin-pyfunc-server 5 | -------------------------------------------------------------------------------- /python/sdk/test/requirements/with-requirements-txt/constraints_in.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - python=3.10.* 3 | - pip: 4 | - -r test/requirements/with-requirements-txt/pyfunc_server_without_version.txt 5 | - -c test/requirements/with-requirements-txt/pyfunc_server_with_version.txt 6 | -------------------------------------------------------------------------------- /python/sdk/test/requirements/with-requirements-txt/constraints_out.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - python=3.10.* 3 | - pip: 4 | - merlin-pyfunc-server 5 | - -c constraints.txt 6 | -------------------------------------------------------------------------------- /python/sdk/test/requirements/with-requirements-txt/empty.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/sdk/test/requirements/with-requirements-txt/empty.txt -------------------------------------------------------------------------------- /python/sdk/test/requirements/with-requirements-txt/empty_in.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - python=3.10.* 3 | - pip: 4 | - -r test/requirements/with-requirements-txt/empty.txt 5 | -------------------------------------------------------------------------------- /python/sdk/test/requirements/with-requirements-txt/empty_out.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - python=3.10.* 3 | - pip: 4 | - merlin-pyfunc-server<0.42.0 5 | -------------------------------------------------------------------------------- /python/sdk/test/requirements/with-requirements-txt/no_pyfunc_server.txt: -------------------------------------------------------------------------------- 1 | cloudpickle==2.0.0 2 | scikit-learn==1.4.0 3 | tensorflow==2.16.1 4 | -------------------------------------------------------------------------------- /python/sdk/test/requirements/with-requirements-txt/no_pyfunc_server_in.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - python=3.10.* 3 | - pip: 4 | - -r test/requirements/with-requirements-txt/no_pyfunc_server.txt 5 | -------------------------------------------------------------------------------- /python/sdk/test/requirements/with-requirements-txt/no_pyfunc_server_out.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - python=3.10.* 3 | - pip: 4 | - merlin-pyfunc-server<0.42.0 5 | - cloudpickle==2.0.0 6 | - scikit-learn==1.4.0 7 | - tensorflow==2.16.1 8 | -------------------------------------------------------------------------------- /python/sdk/test/requirements/with-requirements-txt/pyfunc_server_with_version.txt: -------------------------------------------------------------------------------- 1 | merlin-pyfunc-server==0.50.0 2 | -------------------------------------------------------------------------------- /python/sdk/test/requirements/with-requirements-txt/pyfunc_server_with_version_in.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - python=3.10.* 3 | - pip: 4 | - -r test/requirements/with-requirements-txt/pyfunc_server_with_version.txt 5 | -------------------------------------------------------------------------------- /python/sdk/test/requirements/with-requirements-txt/pyfunc_server_with_version_out.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - python=3.10.* 3 | - pip: 4 | - merlin-pyfunc-server==0.50.0 5 | -------------------------------------------------------------------------------- /python/sdk/test/requirements/with-requirements-txt/pyfunc_server_without_version.txt: -------------------------------------------------------------------------------- 1 | merlin-pyfunc-server 2 | -------------------------------------------------------------------------------- /python/sdk/test/requirements/with-requirements-txt/pyfunc_server_without_version_in.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - python=3.10.* 3 | - pip: 4 | - -r test/requirements/with-requirements-txt/pyfunc_server_without_version.txt 5 | -------------------------------------------------------------------------------- /python/sdk/test/requirements/with-requirements-txt/pyfunc_server_without_version_out.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - python=3.10.* 3 | - pip: 4 | - merlin-pyfunc-server 5 | -------------------------------------------------------------------------------- /python/sdk/test/sklearn-model/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/sdk/test/sklearn-model/.keep -------------------------------------------------------------------------------- /python/sdk/test/sklearn-model/model.joblib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/sdk/test/sklearn-model/model.joblib -------------------------------------------------------------------------------- /python/sdk/test/tensorflow-model/1/saved_model.pb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/sdk/test/tensorflow-model/1/saved_model.pb -------------------------------------------------------------------------------- /python/sdk/test/tensorflow-model/1/variables/variables.data-00000-of-00002: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/sdk/test/tensorflow-model/1/variables/variables.data-00000-of-00002 -------------------------------------------------------------------------------- /python/sdk/test/tensorflow-model/1/variables/variables.data-00001-of-00002: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/sdk/test/tensorflow-model/1/variables/variables.data-00001-of-00002 -------------------------------------------------------------------------------- /python/sdk/test/tensorflow-model/1/variables/variables.index: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/sdk/test/tensorflow-model/1/variables/variables.index -------------------------------------------------------------------------------- /python/sdk/test/transformer/.gitignore: -------------------------------------------------------------------------------- 1 | standard_transformer_multiple_feast.yaml 2 | -------------------------------------------------------------------------------- /python/sdk/test/transformer/feast_enricher.yaml: -------------------------------------------------------------------------------- 1 | transformerConfig: 2 | feast: 3 | - project: merlin 4 | entities: 5 | - name: merlin_test_driver_id 6 | valueType: STRING 7 | jsonPath: "$.driver_id" 8 | features: 9 | - name: merlin_test_driver_features:test_int32 10 | valueType: INT32 11 | defaultValue: "0" 12 | - name: merlin_test_driver_features:test_float 13 | valueType: FLOAT 14 | defaultValue: "0.0" 15 | - name: merlin_test_driver_features:test_double 16 | valueType: DOUBLE 17 | defaultValue: "0.0" 18 | - name: merlin_test_driver_features:test_string 19 | valueType: STRING 20 | defaultValue: "" 21 | -------------------------------------------------------------------------------- /python/sdk/test/transformer/model.pt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/sdk/test/transformer/model.pt -------------------------------------------------------------------------------- /python/sdk/test/transformer/requirements.txt: -------------------------------------------------------------------------------- 1 | torch==1.3.1 2 | torchvision==0.4.2 3 | -------------------------------------------------------------------------------- /python/sdk/test/transformer/sim_exp_resp_feast_wo_tracing.json: -------------------------------------------------------------------------------- 1 | { 2 | "response": { 3 | "instances": { 4 | "columns": [ 5 | "rank", 6 | "driver_id", 7 | "customer_id", 8 | "merlin_test_driver_features:test_int32", 9 | "merlin_test_driver_features:test_float" 10 | ], 11 | "data": [ 12 | [0, "1234", 1111, -1, 0], 13 | [1, "5678", 1111, -1, 0] 14 | ] 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /python/sdk/test/transformer/sim_exp_resp_valid_wo_tracing.json: -------------------------------------------------------------------------------- 1 | { 2 | "response": { 3 | "instances": { 4 | "columns": [ 5 | "customer_id", 6 | "name", 7 | "rank", 8 | "rating", 9 | "vehicle", 10 | "previous_vehicle", 11 | "ep_time_x", 12 | "ep_time_y" 13 | ], 14 | "data": [ 15 | [1111, "driver-2", 2.5, 0.5, 2, 3, 1, -4.100007228307977e-13], 16 | [1111, "driver-1", -2.5, 0.75, 0, 1, 1, -6.364838707220068e-12] 17 | ] 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /python/sdk/test/xgboost-model/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/sdk/test/xgboost-model/.keep -------------------------------------------------------------------------------- /python/sdk/test/xgboost-model/model.bst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/python/sdk/test/xgboost-model/model.bst -------------------------------------------------------------------------------- /scripts/e2e/config/coredns/patch.yaml: -------------------------------------------------------------------------------- 1 | data: 2 | Corefile: | 3 | .:53 { 4 | errors 5 | health 6 | rewrite name regex (.*)\.127\.0\.0\.1\.nip\.io istio-ingressgateway.istio-system.svc.cluster.local 7 | ready 8 | kubernetes cluster.local in-addr.arpa ip6.arpa { 9 | pods insecure 10 | fallthrough in-addr.arpa ip6.arpa 11 | } 12 | hosts /etc/coredns/NodeHosts { 13 | ttl 60 14 | reload 15s 15 | fallthrough 16 | } 17 | prometheus :9153 18 | forward . /etc/resolv.conf 19 | cache 30 20 | loop 21 | reload 22 | loadbalance 23 | } 24 | -------------------------------------------------------------------------------- /scripts/e2e/config/istio/ingress-class.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: IngressClass 3 | metadata: 4 | name: istio 5 | spec: 6 | controller: istio.io/ingress-controller 7 | -------------------------------------------------------------------------------- /scripts/e2e/config/istio/ingress-gateway.yaml: -------------------------------------------------------------------------------- 1 | autoscaling: 2 | enabled: false 3 | 4 | resources: 5 | requests: 6 | cpu: 50m 7 | memory: 64Mi 8 | -------------------------------------------------------------------------------- /scripts/e2e/config/istio/istiod.yaml: -------------------------------------------------------------------------------- 1 | global: 2 | proxy: 3 | autoInject: disabled 4 | omitSidecarInjectorConfigMap: false 5 | 6 | meshConfig: 7 | enableTracing: false 8 | enableAutoMtls: false 9 | 10 | pilot: 11 | autoscaleEnabled: false 12 | resources: 13 | requests: 14 | cpu: 10m 15 | memory: 64Mi 16 | -------------------------------------------------------------------------------- /scripts/e2e/config/knative/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - serving-core.yaml 3 | patchesStrategicMerge: 4 | - overlay.yaml 5 | -------------------------------------------------------------------------------- /scripts/e2e/config/kserve/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - kserve.yaml 3 | patchesStrategicMerge: 4 | - overlay.yaml 5 | -------------------------------------------------------------------------------- /scripts/e2e/config/minio/values.yaml: -------------------------------------------------------------------------------- 1 | replicas: 4 2 | 3 | resources: 4 | requests: 5 | cpu: 10m 6 | memory: 64Mi 7 | limits: ~ 8 | 9 | accessKey: YOURACCESSKEY 10 | secretKey: YOURSECRETKEY 11 | 12 | rootUser: YOURACCESSKEY 13 | rootPassword: YOURSECRETKEY 14 | 15 | ingress: 16 | enabled: true 17 | annotations: 18 | kubernetes.io/ingress.class: istio 19 | path: / 20 | hosts: 21 | - minio.minio.127.0.0.1.nip.io 22 | 23 | consoleIngress: 24 | enabled: false 25 | 26 | buckets: 27 | - name: mlflow 28 | policy: none 29 | purge: false 30 | versioning: false 31 | -------------------------------------------------------------------------------- /scripts/e2e/config/mock/message-dumper.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: serving.knative.dev/v1 2 | kind: Service 3 | metadata: 4 | name: message-dumper 5 | namespace: mlp 6 | spec: 7 | template: 8 | metadata: 9 | annotations: 10 | autoscaling.knative.dev/min-scale: "1" 11 | spec: 12 | containers: 13 | - image: gcr.io/knative-releases/knative.dev/eventing-contrib/cmd/event_display 14 | -------------------------------------------------------------------------------- /scripts/e2e/docs/E2E_arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/scripts/e2e/docs/E2E_arch.png -------------------------------------------------------------------------------- /scripts/quick_install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Prerequisites: 4 | # - cluster have been created using k3d 5 | # - cluster have enabled load balancer 6 | 7 | export CLUSTER_NAME=merlin-cluster 8 | 9 | cd e2e; ./setup-cluster.sh $CLUSTER_NAME 10 | 11 | export INGRESS_HOST=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}') 12 | export DOCKER_REGISTRY=ghcr.io/caraml-dev 13 | export VERSION=0.33.0 14 | export GIT_REF=v0.33.0 15 | export MERLIN_CHART_VERSION=0.13.18 16 | export OAUTH_CLIENT_ID="" 17 | 18 | cd e2e; ./deploy-merlin.sh $INGRESS_HOST $DOCKER_REGISTRY $VERSION $GIT_REF $MERLIN_CHART_VERSION $OAUTH_CLIENT_ID 19 | -------------------------------------------------------------------------------- /ui/.dockerignore: -------------------------------------------------------------------------------- 1 | /coverage 2 | /build 3 | 4 | # misc 5 | .DS_Store 6 | .env 7 | .env.local 8 | .env.development.local 9 | .env.test.local 10 | .env.production.local 11 | .env.production 12 | 13 | npm-debug.log* 14 | yarn-debug.log* 15 | yarn-error.log* 16 | -------------------------------------------------------------------------------- /ui/.env.development: -------------------------------------------------------------------------------- 1 | REACT_APP_MERLIN_API=http://localhost:8080/v1 2 | REACT_APP_MLP_API=http://localhost:8000/v1 3 | REACT_APP_HOMEPAGE=/merlin 4 | 5 | REACT_APP_ALERT_ENABLED=false 6 | REACT_APP_MONITORING_DASHBOARD_ENABLED=false 7 | 8 | REACT_APP_OAUTH_CLIENT_ID= 9 | 10 | REACT_APP_DOCKER_REGISTRIES=ghcr.io/gojek,gcr.io/kubeflow-ci 11 | -------------------------------------------------------------------------------- /ui/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env 17 | .env.local 18 | .env.development.local 19 | .env.test.local 20 | .env.production.local 21 | .env.production 22 | **/.idea 23 | 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | version.txt -------------------------------------------------------------------------------- /ui/public/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/ui/public/android-chrome-192x192.png -------------------------------------------------------------------------------- /ui/public/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/ui/public/android-chrome-512x512.png -------------------------------------------------------------------------------- /ui/public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/ui/public/apple-touch-icon.png -------------------------------------------------------------------------------- /ui/public/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #00aba9 7 | 8 | 9 | -------------------------------------------------------------------------------- /ui/public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/ui/public/favicon-16x16.png -------------------------------------------------------------------------------- /ui/public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/ui/public/favicon-32x32.png -------------------------------------------------------------------------------- /ui/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/ui/public/favicon.ico -------------------------------------------------------------------------------- /ui/public/mstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/ui/public/mstile-144x144.png -------------------------------------------------------------------------------- /ui/public/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/ui/public/mstile-150x150.png -------------------------------------------------------------------------------- /ui/public/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/ui/public/mstile-310x150.png -------------------------------------------------------------------------------- /ui/public/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/ui/public/mstile-310x310.png -------------------------------------------------------------------------------- /ui/public/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caraml-dev/merlin/fbbdd3ae9bd37621afe7ae199dadfab7b972d701/ui/public/mstile-70x70.png -------------------------------------------------------------------------------- /ui/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | -------------------------------------------------------------------------------- /ui/public/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Merlin", 3 | "short_name": "Merlin", 4 | "icons": [ 5 | { 6 | "src": "/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#ffffff", 17 | "background_color": "#ffffff" 18 | } -------------------------------------------------------------------------------- /ui/src/PrivateLayout.scss: -------------------------------------------------------------------------------- 1 | .main-component-layout { 2 | padding-top: 49px; 3 | } 4 | -------------------------------------------------------------------------------- /ui/src/index.js: -------------------------------------------------------------------------------- 1 | import("./bootstrap"); 2 | -------------------------------------------------------------------------------- /ui/src/mocks/data/container.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kfserving-container", 3 | "pod_name": "pod-deploymeqdslf", 4 | "namespace": "default-fraud", 5 | "cluster": "models", 6 | "gcp_project": "", 7 | "version_endpoint_id": "98507933-03eb-44ba-8f9e-1ef5dcb96c79" 8 | } -------------------------------------------------------------------------------- /ui/src/mocks/data/model.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 2, 3 | "project_id": 1, 4 | "mlflow_experiment_id": 2, 5 | "name": "test-model", 6 | "type": "other", 7 | "mlflow_url": "", 8 | "created_at": "2019-09-11T11:07:28.19984Z", 9 | "updated_at": "2019-09-11T11:07:28.19984Z" 10 | } -------------------------------------------------------------------------------- /ui/src/mocks/data/modelEndpointAlert.json: -------------------------------------------------------------------------------- 1 | { 2 | "model_id": 1, 3 | "model_endpoint_id": 1, 4 | "environment_name": "id-dev", 5 | "team_name": "datascience", 6 | "alert_conditions": [ 7 | { 8 | "enabled": true, 9 | "metric_type": "throughput", 10 | "severity": "WARNING", 11 | "target": 1000, 12 | "percentile": 0, 13 | "unit": "" 14 | } 15 | ], 16 | "created_at": "2020-04-24T04:45:41.833445Z", 17 | "updated_at": "2020-04-24T04:45:41.833445Z" 18 | } 19 | -------------------------------------------------------------------------------- /ui/src/mocks/data/modelList.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "id": 1, 3 | "project_id": 1, 4 | "mlflow_experiment_id": 1, 5 | "name": "wine-model-sklearn", 6 | "type": "other", 7 | "mlflow_url": "", 8 | "created_at": "2019-09-11T10:55:41.754971Z", 9 | "updated_at": "2019-09-11T10:55:41.754971Z" 10 | }, 11 | { 12 | "id": 2, 13 | "project_id": 1, 14 | "mlflow_experiment_id": 2, 15 | "name": "test-model", 16 | "type": "other", 17 | "mlflow_url": "", 18 | "created_at": "2019-09-11T11:07:28.19984Z", 19 | "updated_at": "2019-09-11T11:07:28.19984Z" 20 | } 21 | ] -------------------------------------------------------------------------------- /ui/src/mocks/data/noBody.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /ui/src/mocks/data/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 2, 3 | "name": "sample", 4 | "mlflow_tracking_url": "http://sample.mlflow.global.merlin.dev/#/", 5 | "created_at": "2019-08-23T05:49:43.214Z", 6 | "updated_at": "2019-08-24T06:49:43.214Z" 7 | } -------------------------------------------------------------------------------- /ui/src/mocks/data/projectList.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "id": 1, 3 | "name": "sample", 4 | "mlflow_tracking_url": "http://sample.mlflow.global.merlin.dev/#/", 5 | "created_at": "2019-08-17T05:49:43.214Z", 6 | "updated_at": "2019-08-17T06:49:43.214Z" 7 | }, 8 | { 9 | "id": 2, 10 | "name": "project-x", 11 | "mlflow_tracking_url": "http://project-x.mlflow.global.merlin.dev/#/", 12 | "created_at": "2019-08-23T05:49:43.214Z", 13 | "updated_at": "2019-08-24T06:49:43.214Z" 14 | } 15 | ] -------------------------------------------------------------------------------- /ui/src/pages/job/components/JobErrorMessage.js: -------------------------------------------------------------------------------- 1 | import { EuiCodeBlock } from "@elastic/eui"; 2 | import { ConfigSection, ConfigSectionPanel } from "../../../components/section"; 3 | 4 | const JobErrorMessage = ({ error }) => { 5 | return ( 6 | 7 | 8 | 9 | {error} 10 | 11 | 12 | 13 | ); 14 | }; 15 | 16 | export default JobErrorMessage; 17 | -------------------------------------------------------------------------------- /ui/src/pages/job/components/JobLog.js: -------------------------------------------------------------------------------- 1 | import { useParams } from "react-router-dom"; 2 | import { ContainerLogsView } from "../../../components/logs/ContainerLogsView"; 3 | 4 | const JobLog = ({ model }) => { 5 | const { projectId, versionId, jobId } = useParams(); 6 | 7 | const containerURL = `/models/${model.id}/versions/${versionId}/jobs/${jobId}/containers`; 8 | 9 | return ( 10 | 17 | ); 18 | }; 19 | 20 | export default JobLog; 21 | -------------------------------------------------------------------------------- /ui/src/pages/job/components/OtherConfig.js: -------------------------------------------------------------------------------- 1 | import { EuiDescriptionList } from "@elastic/eui"; 2 | import React, { Fragment } from "react"; 3 | import { ConfigSectionPanelTitle } from "../../../components/section"; 4 | 5 | const OtherConfig = ({ job }) => { 6 | const items = [ 7 | { title: "Docker Image", description: job.config.image_ref }, 8 | { title: "Service Account", description: job.config.service_account_name }, 9 | ]; 10 | return ( 11 | 12 | 13 | 14 | 20 | 21 | ); 22 | }; 23 | 24 | export default OtherConfig; 25 | -------------------------------------------------------------------------------- /ui/src/pages/job/utils/bigquery.js: -------------------------------------------------------------------------------- 1 | export function getBigQueryDashboardUrl(tableId) { 2 | const segment = tableId.split("."); 3 | const project = segment[0]; 4 | const dataset = segment[1]; 5 | const table = segment[2]; 6 | return `https://console.cloud.google.com/bigquery?project=${project}&p=${project}&d=${dataset}&t=${table}&page=table`; 7 | } 8 | -------------------------------------------------------------------------------- /ui/src/pages/version/components/forms/components/DeploymentSummary.js: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from "react"; 2 | import { EuiSpacer } from "@elastic/eui"; 3 | import { CostEstimationPanel } from "./CostEstimationPanel"; 4 | 5 | export const DeploymentSummary = ({ 6 | actionTitle = "Deploy", 7 | modelName, 8 | versionId, 9 | versionEndpoint, 10 | }) => { 11 | return ( 12 | 13 |

14 | You're about to {actionTitle.toLowerCase()} a new endpoint for model{" "} 15 | {modelName} version {versionId}. 16 |

17 | 18 | 19 |
20 | ); 21 | }; 22 | -------------------------------------------------------------------------------- /ui/src/pages/version/components/forms/components/feast_config/components/FeastInputCard.scss: -------------------------------------------------------------------------------- 1 | .euiCard.euiCard--feastRetrievalInputCard { 2 | .euiCard__children { 3 | margin-top: 0 !important; 4 | } 5 | 6 | .euiCard__description { 7 | display: none !important; 8 | } 9 | 10 | .euiFlexGroup--removeButton { 11 | margin-bottom: -12px !important; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ui/src/pages/version/components/forms/components/transformer/RowCell.scss: -------------------------------------------------------------------------------- 1 | .euiTableRowCell { 2 | vertical-align: top !important; 3 | } 4 | -------------------------------------------------------------------------------- /ui/src/pages/version/components/forms/components/transformer/components/AddButton.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { EuiButton, EuiText, EuiToolTip } from "@elastic/eui"; 3 | 4 | export const AddButton = ({ 5 | title, 6 | description, 7 | onClick, 8 | fullWidth = true, 9 | titleSize = "s", 10 | disabled = false 11 | }) => { 12 | return ( 13 | 14 | 19 | {title} 20 | 21 | 22 | ); 23 | }; 24 | -------------------------------------------------------------------------------- /ui/src/pages/version/components/forms/components/transformer/components/TransformationGraph.scss: -------------------------------------------------------------------------------- 1 | .transformationGraph { 2 | padding: 10px; 3 | // border: 1px solid #EEEEEE; 4 | 5 | text { 6 | font-family: "Roboto Mono", Consolas, Menlo, Courier, monospace; 7 | font-size: 11px; 8 | } 9 | 10 | .nodes { 11 | fill: #ffffff; 12 | } 13 | 14 | .label-container { 15 | stroke: #333333; 16 | } 17 | 18 | .label { 19 | fill: #333333; 20 | } 21 | 22 | .edgePath { 23 | stroke: black; 24 | } 25 | } 26 | 27 | .modelServiceNode { 28 | fill: #006bb4; 29 | .label { 30 | fill: #ffffff; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ui/src/pages/version/components/forms/components/transformer/components/simulation/constants.js: -------------------------------------------------------------------------------- 1 | export const simulationIcon = "analyzeEvent"; 2 | -------------------------------------------------------------------------------- /ui/src/pages/version/components/forms/steps/Pipeline.scss: -------------------------------------------------------------------------------- 1 | .config::-webkit-scrollbar { 2 | display: none; /* Safari and Chrome */ 3 | } 4 | 5 | .config-sidebar { 6 | overflow-y: auto; 7 | position: sticky; 8 | top: 75px !important; 9 | 10 | .euiSideNavItemButton__label { 11 | font-size: 0.75rem; 12 | line-height: 2; 13 | } 14 | } 15 | 16 | .toggle-panel-button { 17 | overflow-y: auto; 18 | position: sticky; 19 | top: 90px !important; 20 | } 21 | 22 | .graph-sidebar { 23 | overflow-y: auto; 24 | position: sticky; 25 | top: 75px !important; 26 | } 27 | 28 | .preprocessing { 29 | background-color: #eaf9ff; 30 | } 31 | 32 | .postprocessing { 33 | background-color: #d2f1fe; 34 | } 35 | 36 | .simulation { 37 | background-color: #eaf8ff; 38 | } 39 | -------------------------------------------------------------------------------- /ui/src/providers/docker/context.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { appConfig } from "../../config"; 3 | 4 | const DockerRegistriesContext = React.createContext([]); 5 | 6 | export const DockerRegistriesContextProvider = ({ children }) => { 7 | const registries = [ 8 | ...appConfig.dockerRegistries.map(registry => ({ 9 | value: registry, 10 | inputDisplay: registry 11 | })), 12 | { 13 | value: "docker.io", 14 | inputDisplay: "Docker Hub" 15 | } 16 | ]; 17 | 18 | return ( 19 | 20 | {children} 21 | 22 | ); 23 | }; 24 | 25 | export default DockerRegistriesContext; 26 | -------------------------------------------------------------------------------- /ui/src/providers/environments/context.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useMerlinApi } from "../../hooks/useMerlinApi"; 3 | 4 | const EnvironmentsContext = React.createContext([]); 5 | 6 | export const EnvironmentsContextProvider = ({ children }) => { 7 | const [{ data: environments }] = useMerlinApi("/environments", {}, []); 8 | 9 | return ( 10 | 11 | {children} 12 | 13 | ); 14 | }; 15 | 16 | export default EnvironmentsContext; 17 | -------------------------------------------------------------------------------- /ui/src/providers/feast/FeastProjectsContext.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { feastEndpoints, useFeastApi } from "../../hooks/useFeastApi"; 3 | 4 | const FeastProjectsContext = React.createContext([]); 5 | 6 | export const FeastProjectsContextProvider = ({ children }) => { 7 | const [{ data: projects }] = useFeastApi( 8 | feastEndpoints.listProjects, 9 | { method: "GET", muteError: true }, 10 | {}, 11 | true 12 | ); 13 | 14 | return ( 15 | 16 | {children} 17 | 18 | ); 19 | }; 20 | 21 | export default FeastProjectsContext; 22 | -------------------------------------------------------------------------------- /ui/src/services/logger/Logger.js: -------------------------------------------------------------------------------- 1 | const objectAssignDeep = require(`object-assign-deep`); 2 | 3 | class LoggerConfig { 4 | constructor() { 5 | this.enabled = false; 6 | this.mode = ""; 7 | } 8 | } 9 | 10 | class PredictionLoggerConfig { 11 | constructor() { 12 | this.enabled = false; 13 | this.raw_features_table = ""; 14 | this.entities_table = ""; 15 | } 16 | } 17 | 18 | export class Logger { 19 | constructor() { 20 | this.model = new LoggerConfig(); 21 | this.transformer = new LoggerConfig(); 22 | this.prediction = new PredictionLoggerConfig(); 23 | } 24 | 25 | static fromJson(json) { 26 | const logger = objectAssignDeep(new Logger(), json); 27 | return logger; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ui/src/services/logger/Logger.test.js: -------------------------------------------------------------------------------- 1 | import { Logger } from "./Logger"; 2 | 3 | test("test new Logger()", () => { 4 | const logger = new Logger(); 5 | 6 | expect(logger.model).toEqual({ enabled: false, mode: "" }); 7 | expect(logger.transformer).toEqual({ enabled: false, mode: "" }); 8 | }); 9 | -------------------------------------------------------------------------------- /ui/src/services/transformer/TransformerConfig.test.js: -------------------------------------------------------------------------------- 1 | import { Config } from "./TransformerConfig"; 2 | 3 | const fs = require("fs"); 4 | const path = require("path"); 5 | const yaml = require("js-yaml"); 6 | 7 | test("test Config.fromJson()", () => { 8 | const content = fs 9 | .readFileSync(path.join(__dirname + "/testdata/standard_transformer.yaml")) 10 | .toString(); 11 | const config = Config.fromJson(yaml.load(content)); 12 | 13 | expect(config.transformerConfig).toBeDefined(); 14 | 15 | expect(config.transformerConfig.feast).toBeUndefined(); 16 | expect(config.transformerConfig.preprocess).toBeDefined(); 17 | expect(config.transformerConfig.postprocess).toBeDefined(); 18 | }); 19 | --------------------------------------------------------------------------------