def create(cluster: Cluster, name: str, modelversion_id: int, config: MetricSpecConfig) -> 'MetricSpec': """ Create MetricSpec and returns corresponding instance. :param cluster: active cluster :param name: name of the metric :param modelversion_id: ModelVersion for which to create a MetricSpec :param config: MetricSpecConfig, describing MetricSpec :return: metricSpec """ metric_spec_json = { 'name': name, 'modelVersionId': modelversion_id, 'config': { 'modelVersionId': config.modelversion_id, 'threshold': config.threshold, 'thresholdCmpOperator': { 'kind': config.threshold_op } } } resp = cluster.request("POST", MetricSpec._BASE_URL, json=metric_spec_json) handle_request_error( resp, f"Failed to create a MetricSpec for name={name}, modelversion_id={modelversion_id}. {resp.status_code} {resp.text}" ) return MetricSpec._from_json(cluster, resp.json())
def build(self, cluster: Cluster) -> Application: """ Create an Application in your Hydrosphere cluster. :return: Application object """ if not self.stages: raise ValueError("No execution stages were provided") execution_graph = ExecutionGraph(stages=self.stages) application_json = { "name": self.name, "kafkaStreaming": [sp.to_dict() for sp in self.streaming_parameters], "executionGraph": execution_graph.to_dict(), "metadata": self.metadata } resp = cluster.request("POST", Application._BASE_URL, json=application_json) handle_request_error( resp, f"Failed to create an application {self.name}. {resp.status_code} {resp.text}" ) return Application._from_json(cluster, resp.json())
def create( cluster: Cluster, model_name: str, version: int, metadata: Optional[Dict[str, str]] = None, deployment_configuration: Optional[DeploymentConfiguration] = None ) -> 'Servable': """ Deploy an instance of uploaded model version at your cluster. :param deployment_configuration: k8s configurations used to run this servable :param cluster: Cluster connected to Hydrosphere :param model_name: Name of uploaded model :param version: Version of uploaded model :param metadata: Information which you can attach to your servable in a form of Dict[str, str] :raises ServableException: :return: servable """ msg = { "modelName": model_name, "version": version, } if metadata: msg['metadata'] = metadata if deployment_configuration: msg['deploymentConfigName'] = deployment_configuration.name resp = cluster.request('POST', Servable._BASE_URL, json=msg) handle_request_error( resp, f"Failed to create a servable. {resp.status_code} {resp.text}") return Servable._from_json(cluster, resp.json())
def test_training_data_upload(cluster: Cluster, model_version_builder: ModelVersionBuilder, training_data: str): mv: ModelVersion = model_version_builder.build(cluster) mv.training_data = training_data data_upload_response = mv.upload_training_data() data_upload_response.wait(sleep=5) resp = cluster.request("GET", f"/monitoring/profiles/batch/{mv.id}/status") assert "Success" == resp.json()["kind"]
def list(cluster: Cluster) -> List['DeploymentConfiguration']: """ List all deployment configurations :param cluster: :return: """ resp = cluster.request("GET", DeploymentConfiguration._BASE_URL) handle_request_error(resp, f"Failed to get a list of Deployment Configurations - {resp.status_code} {resp.text}") return [DeploymentConfiguration.parse_obj(app_json) for app_json in resp.json()]
def _upload_s3_file(cluster: Cluster, modelversion_id: int, path: str) -> requests.Response: """ Internal method for submitting training data from S3 to Hydrosphere. :param cluster: Cluster instance :param url: url to which submit S3 path :param path: S3 path to training data """ url = f'/monitoring/profiles/batch/{modelversion_id}/s3' return cluster.request("POST", url, json={"path": path})
def find(cluster: Cluster, name: str) -> 'DeploymentConfiguration': """ Search a deployment configuration by name :param cluster: :param name: :return: """ resp = cluster.request("GET", f"{DeploymentConfiguration._BASE_URL}/{name}") handle_request_error(resp, f"Failed to find Deployment Configuration named {name} {resp.status_code} {resp.text}") return DeploymentConfiguration.parse_obj(resp.json())
def delete(cluster: Cluster, name: str) -> dict: """ Delete deployment configuration from Hydrosphere cluster :param cluster: Active Cluster :param name: Deployment configuration name :return: json response from the server """ resp = cluster.request("DELETE", f"{DeploymentConfiguration._BASE_URL}/{name}") handle_request_error(resp, f"Failed to delete Deployment Configuration named {name}: {resp.status_code} {resp.text}") return resp
def list(cluster: Cluster) -> List['MetricSpec']: """ Send request and returns list with all available metric specs. :param cluster: active cluster :return: list with all available metric specs """ resp = cluster.request("GET", MetricSpec._BASE_URL) handle_request_error( resp, f"Failed to list MetricSpecs. {resp.status_code} {resp.text}") return [MetricSpec._from_json(cluster, x) for x in resp.json()]
def delete(cluster: Cluster, id: int) -> dict: """ Delete MetricSpec. :return: result of deletion """ resp = cluster.request("DELETE", f"{MetricSpec._BASE_URL}/{id}") handle_request_error( resp, f"Failed to delete MetricSpec for id={id}. {resp.status_code} {resp.text}" ) return resp.json()
def _upload_local_file(cluster: Cluster, modelversion_id: int, path: str, chunk_size=1024) -> requests.Response: """ Internal method for uploading local training data to Hydrosphere. :param cluster: active cluster :param modelversion_id: modelversion_id for which to upload training data :param path: path to a local file :param chunk_size: chunk size to use for streaming """ gen = read_in_chunks(path, chunk_size) url = f'/monitoring/profiles/batch/{modelversion_id}' return cluster.request("POST", url, data=gen, stream=True)
def find(cluster: Cluster, name: str, version: int) -> 'ModelVersion': """ Find a ModelVersion on the cluster by model name and a version. :param cluster: active cluster :param name: name of the model :param version: version of the model :return: ModelVersion object """ resp = cluster.request("GET", f"{ModelVersion._BASE_URL}/version/{name}/{version}") handle_request_error( resp, f"Failed to find model_version for name={name}, version={version}. {resp.status_code} {resp.text}") return ModelVersion._from_json(cluster, resp.json())
def list(cluster: Cluster) -> List['ModelVersion']: """ List all model versions on the cluster. :param cluster: active cluster :return: list of ModelVersions """ resp = cluster.request("GET", f"{ModelVersion._BASE_URL}/version") handle_request_error( resp, f"Failed to list model versions. {resp.status_code} {resp.text}") return [ModelVersion._from_json(cluster, modelversion_json) for modelversion_json in resp.json()]
def find_by_id(cluster: Cluster, id: int) -> 'MetricSpec': """ Find MetricSpec by id. :param cluster: active cluster :param id: :return: MetricSpec """ resp = cluster.request("GET", f"{MetricSpec._BASE_URL}/{id}") handle_request_error( resp, f"Failed to retrieve MetricSpec for id={id}. {resp.status_code} {resp.text}" ) return MetricSpec._from_json(cluster, resp.json())
def list(cluster: Cluster) -> List['Servable']: """ Retrieve a list of all servables available at your cluster :param cluster: Hydrosphere cluster :return: List of all Servables available at your cluster """ resp = cluster.request("GET", Servable._BASE_URL) handle_request_error( resp, f"Failed to list servables. {resp.status_code} {resp.text}") return [ Servable._from_json(cluster, servable_json) for servable_json in resp.json() ]
def find(cluster: Cluster, application_name: str) -> 'Application': """ Search for an application by name. :param cluster: active cluster :param application_name: application name :return: deserialized application object """ resp = cluster.request("GET", f"{Application._BASE_URL}/{application_name}") handle_request_error( resp, f"Failed to find an application by name={application_name}. {resp.status_code} {resp.text}" ) return Application._from_json(cluster, resp.json())
def find_by_id(cluster: Cluster, id: int) -> 'ModelVersion': """ Find a ModelVersion on the cluster by id. :param cluster: active cluster :param id: model version id :return: ModelVersion object """ resp = cluster.request("GET", f"{ModelVersion._BASE_URL}/version") handle_request_error( resp, f"Failed to retrieve model_version by id={id}. {resp.status_code} {resp.text}") for modelversion_json in resp.json(): if modelversion_json['id'] == id: return ModelVersion._from_json(cluster, modelversion_json) raise BadRequestException(f"Failed to retrieve model_version by id={id}.")
def delete(cluster: Cluster, application_name: str) -> dict: """ Delete an application by name. :param cluster: active cluster :param application_name: application name :return: response from the cluster """ resp = cluster.request("DELETE", f"{Application._BASE_URL}/{application_name}") handle_request_error( resp, f"Failed to delete application for name={application_name}. {resp.status_code} {resp.text}" ) return resp.json()
def find_by_name(cluster: Cluster, servable_name: str) -> 'Servable': """ Finds a serving servable in a cluster :param cluster: active cluster :param servable_name: a name of the servable :raises ServableException: :return: Servable """ resp = cluster.request("GET", f"{Servable._BASE_URL}/{servable_name}") handle_request_error( resp, f"Failed to find servable for name={servable_name}. {resp.status_code} {resp.text}" ) return Servable._from_json(cluster, resp.json())
def find_by_modelversion(cluster: Cluster, modelversion_id: int) -> List['MetricSpec']: """ Find MetricSpecs by model version. :param cluster: active cluster :param modelversion_id: ModelVersions for which to return metrics. :return: list of MetricSpec objects """ resp = cluster.request( "GET", f"{MetricSpec._BASE_URL}/modelversion/{modelversion_id}") handle_request_error( resp, f"Failed to list MetricSpecs for modelversion_id={modelversion_id}. {resp.status_code} {resp.text}" ) return [MetricSpec._from_json(cluster, x) for x in resp.json()]
def list(cluster: Cluster) -> List['Application']: """ List all available applications from the cluster. :param cluster: active cluster :return: deserialized list of application objects """ resp = cluster.request("GET", Application._BASE_URL) handle_request_error( resp, f"Failed to list all applications. {resp.status_code} {resp.text}") applications = [ Application._from_json(cluster, app_json) for app_json in resp.json() ] return applications
def build(self, cluster: Cluster) -> DeploymentConfiguration: """ Create the Deployment Configuration in your Hydrosphere cluster. :return: Deployment Configuration object from the Hydrosphere cluster """ config = DeploymentConfiguration( name=self.name, hpa=self.hpa, pod=self.pod_spec, container=self.container_spec, deployment=self.deployment_spec ) resp = cluster.request("POST", DeploymentConfiguration._BASE_URL, json=config.to_dict()) handle_request_error(resp, f"Failed to upload new Deployment Configuration. {resp.status_code} {resp.text}") return DeploymentConfiguration.parse_obj(resp.json())
def delete(cluster: Cluster, servable_name: str) -> dict: """ Shut down and delete servable instance. :param cluster: active cluster :param servable_name: name of the servable :return: json response from serve .. warnings also:: Use with caution. Predictors previously associated with this servable will not be able to connect to it. """ resp = cluster.request("DELETE", f"{Servable._BASE_URL}/{servable_name}") handle_request_error( resp, f"Failed to delete the servable with name={servable_name}. {resp.status_code} {resp.text}" ) return resp.json()
def build_external(self, cluster: Cluster) -> 'ModelVersion': """ Create an external ModelVersion on the cluster. :param cluster: active cluster :return: ModelVersion object """ if self.signature is None: raise ValueError("signature is not specified for the model") model = { "name": self.name, "signature": ModelSignature_to_signature_dict(self.signature), "metadata": self.metadata } resp = cluster.request("POST", "/api/v2/externalmodel", json=model) handle_request_error( resp, f"Failed to create an external model. {resp.status_code} {resp.text}") json_dict = resp.json() json_dict['isExternal'] = True return ModelVersion._from_json(cluster, json_dict)
def build(self, cluster: Cluster) -> 'ModelVersion': """ Upload local model instance to the cluster. :param cluster: active cluster :return: ModelVersion object """ if self.runtime is None: raise ValueError("runtime is not specified for the model") if self.signature is None: raise ValueError("signature is not specified for the model") meta = { "name": self.name, "runtime": self.runtime.dict(), "modelSignature": ModelSignature_to_signature_dict(self.signature), "installCommand": self.install_command, "metadata": self.metadata } if self.monitoring_configuration: meta.update({"monitoringConfiguration": self.monitoring_configuration.to_dict()}) tarpath = self.__create_tarball() encoder = MultipartEncoder( fields={ "payload": ("filename", open(tarpath, "rb")), "metadata": json.dumps(meta) } ) resp = cluster.request("POST", "/api/v2/model/upload", data=encoder, headers={'Content-Type': encoder.content_type}) handle_request_error( resp, f"Failed to upload local model. {resp.status_code} {resp.text}") modelversion = ModelVersion._from_json(cluster, resp.json()) modelversion.training_data = self.training_data return modelversion