diff --git a/api/server/swagger_server/controllers_impl/inference_service_controller_impl.py b/api/server/swagger_server/controllers_impl/inference_service_controller_impl.py index 4b8e1ba0..565abfc8 100644 --- a/api/server/swagger_server/controllers_impl/inference_service_controller_impl.py +++ b/api/server/swagger_server/controllers_impl/inference_service_controller_impl.py @@ -19,6 +19,8 @@ from swagger_server.gateways.kfserving_client import post_service from swagger_server.gateways.kfserving_client import from_client_upload_service +import logging + def get_inferenceservices(id, namespace=None): # noqa: E501 """get_inferenceservices @@ -32,8 +34,20 @@ def get_inferenceservices(id, namespace=None): # noqa: E501 :rtype: ApiInferenceservice """ - single_service = get_all_services(id, namespace=namespace) - return single_service, 200 + log = logging.getLogger("inf_serv") + # Attempt to find the id in a model mesh predictor + try: + single_service = get_all_services(id, namespace=namespace, group="serving.kserve.io", version="v1alpha1", plural="predictors") + return single_service, 200 + except: + pass + # Attempt to find the id in a kserve inferenceservice + try: + single_service = get_all_services(id, namespace=namespace, group="serving.kserve.io", version="v1beta1", plural="inferenceservices") + return single_service, 200 + except Exception as err: + log.exception("Error when trying to find an inferenceservice: ") + return str(err), 500 def list_inferenceservices(page_token=None, page_size=None, sort_by=None, filter=None, namespace=None): # noqa: E501 @@ -54,8 +68,16 @@ def list_inferenceservices(page_token=None, page_size=None, sort_by=None, filter :rtype: ApiListInferenceservicesResponse """ - all_services = get_all_services(namespace=namespace) - return all_services, 200 + log = logging.getLogger("inf_serv") + try: + # Combine the list of items from the modelmesh predictors and kserve inferenceservices + all_mm_services = get_all_services(namespace=namespace, group="serving.kserve.io", version="v1alpha1", plural="predictors") + all_k_services = get_all_services(namespace=namespace, group="serving.kserve.io", version="v1beta1", plural="inferenceservices") + all_mm_services['items'] = all_mm_services['items'] + all_k_services['items'] + return all_mm_services, 200 + except Exception as err: + log.exception("Error when trying to list inferenceservices: ") + return str(err), 500 def create_service(body, namespace=None): # noqa: E501 @@ -70,8 +92,13 @@ def create_service(body, namespace=None): # noqa: E501 :rtype: ApiInferenceservice """ - created_service = post_service(body, namespace=namespace) - return created_service, 200 + log = logging.getLogger("inf_serv") + try: + created_service = post_service(body, namespace=namespace) + return created_service, 200 + except Exception as err: + log.exception("Error when deploying an inferenceservice: ") + return str(err), 500 def upload_service(uploadfile, name=None, namespace=None): # noqa: E501 @@ -88,5 +115,11 @@ def upload_service(uploadfile, name=None, namespace=None): # noqa: E501 :rtype: ApiComponent """ - uploaded_service = from_client_upload_service(uploadfile, namespace=namespace) - return uploaded_service, 200 + log = logging.getLogger("inf_serv") + try: + uploaded_service = from_client_upload_service(upload_file=uploadfile, namespace=namespace) + return uploaded_service, 200 + except Exception as err: + log.exception("Error when deploying an inferenceservice: ") + return str(err), 500 + diff --git a/api/server/swagger_server/gateways/kfserving_client.py b/api/server/swagger_server/gateways/kfserving_client.py index 0587d382..aebec75d 100644 --- a/api/server/swagger_server/gateways/kfserving_client.py +++ b/api/server/swagger_server/gateways/kfserving_client.py @@ -12,41 +12,82 @@ # See the License for the specific language governing permissions and # limitations under the License. -from kfserving import KFServingClient import yaml +from kubernetes import client, config -def get_kfserving_client(): - client = KFServingClient() - return client +def get_all_services(name=None, namespace=None, group=None, version=None, plural=None): + config.load_incluster_config() + api = client.CustomObjectsApi() -def get_all_services(name=None, namespace=None): - client = get_kfserving_client() if not namespace: namespace = 'default' - return client.get(name, namespace=namespace) + if name is None: + resource = api.list_namespaced_custom_object( + group=group, + version=version, + namespace=namespace, + plural=plural, + ) + else: + resource = api.get_namespaced_custom_object( + group=group, + version=version, + namespace=namespace, + name=name, + plural=plural, + ) + return resource + + +def post_service(inferenceservice=None, namespace=None, group=None, version=None, plural=None): + + config.load_incluster_config() + api = client.CustomObjectsApi() -def post_service(inferenceservice=None, namespace=None): - client = get_kfserving_client() service_dict = inferenceservice.to_dict() - name = service_dict['metadata']['name'] + # Get resource information from the dict + version_split = service_dict['apiVersion'].split("/") + group = version_split[0] + version = version_split[1] + plural = service_dict['kind'].lower() + "s" if not namespace: namespace = service_dict['metadata'].get('namespace', 'default') - try: - return client.create(service_dict, namespace=namespace) - except: - return client.patch(name, service_dict, namespace=namespace) + + # create the resource + ns_obj = api.create_namespaced_custom_object( + group=group, + version=version, + namespace=namespace, + plural=plural, + body=service_dict, + ) + return ns_obj -def from_client_upload_service(upload_file=None, namespace=None): - client = get_kfserving_client() +def from_client_upload_service(upload_file=None, namespace=None, group=None, version=None, plural=None): + + config.load_incluster_config() + api = client.CustomObjectsApi() + yaml_object = yaml.safe_load(upload_file) + # Get resource information from the yaml name = yaml_object['metadata']['name'] + version_split = yaml_object['apiVersion'].split("/") + group = version_split[0] + version = version_split[1] + plural = yaml_object['kind'].lower() + "s" if not namespace: namespace = yaml_object['metadata'].get('namespace', 'default') - try: - return client.create(yaml_object, namespace=namespace) - except: - return client.patch(name, yaml_object, namespace=namespace) + + # create the resource + ns_obj = api.create_namespaced_custom_object( + group=group, + version=version, + namespace=namespace, + plural=plural, + body=yaml_object, + ) + return ns_obj \ No newline at end of file diff --git a/dashboard/origin-mlx/src/components/Detail/KFServingDetail.tsx b/dashboard/origin-mlx/src/components/Detail/KFServingDetail.tsx index ae48163e..0d6aca2e 100644 --- a/dashboard/origin-mlx/src/components/Detail/KFServingDetail.tsx +++ b/dashboard/origin-mlx/src/components/Detail/KFServingDetail.tsx @@ -99,6 +99,8 @@ export default class KFServingDetail extends React.Component) => { this.setState({...this.state, file: e.currentTarget.files[0]}) @@ -150,7 +152,8 @@ export default class KFServingDetail extends React.Component + : + } return ( <> diff --git a/dashboard/origin-mlx/src/pages/KFServingFeaturedPage/KFServingFeatured.tsx b/dashboard/origin-mlx/src/pages/KFServingFeaturedPage/KFServingFeatured.tsx index 56ad86a5..5c09b18d 100644 --- a/dashboard/origin-mlx/src/pages/KFServingFeaturedPage/KFServingFeatured.tsx +++ b/dashboard/origin-mlx/src/pages/KFServingFeaturedPage/KFServingFeatured.tsx @@ -42,6 +42,11 @@ const styles = { function MetaFeatured(props: MetaFeaturedProps) { const { assets, classes } = props + console.log("Asset:") + console.log(props.assets) + + //return (

Placeholder

) + return (
{assets.map(asset => { const name = asset.metadata.name; - var runningStatus = asset.status?.conditions[0]?.status || "" - var statusIcon = "" + + let runningStatus = "" + let statusIcon = "" + if (asset.status.conditions) { + runningStatus = asset.status?.conditions[0]?.status || "" - if (asset.status) { - for (var i=0; i< asset.status.conditions.length; i++) { - if (asset.status.conditions[i].type === "Ready") { - const index = asset.status.conditions.indexOf(asset.status.conditions[i]) - runningStatus = asset.status.conditions[index].status - if (runningStatus === 'True') { - statusIcon = checkmark - } - else { - statusIcon = cancel + if (asset.status) { + for (var i=0; i< asset.status.conditions.length; i++) { + if (asset.status.conditions[i].type === "Ready") { + const index = asset.status.conditions.indexOf(asset.status.conditions[i]) + runningStatus = asset.status.conditions[index].status + if (runningStatus === 'True') { + statusIcon = checkmark + } + else { + statusIcon = cancel + } } } } } + else { + runningStatus = asset.status.available + if (runningStatus === 'true') + statusIcon = checkmark + else + statusIcon = cancel + } const description = asset.kind; const tag = Object.keys(asset.spec).join(",") const link = "inferenceservices" - const predictor = asset.spec.predictor - const framework = predictor.tensorflow ? "tensorflow" - : predictor.keras ? "keras" - : predictor.sklearn ? "sklearn" - : predictor["scikit-learn"] ? "sklearn" - : predictor.pytorch ? "pytorch" : "custom" + let predictor = asset.spec.predictor + let framework = "" + if (asset.spec.predictor) { + framework = predictor.tensorflow ? "tensorflow" + : predictor.keras ? "keras" + : predictor.sklearn ? "sklearn" + : predictor["scikit-learn"] ? "sklearn" + : predictor.pytorch ? "pytorch" : "custom" + } + else { + framework = asset.spec.modelType.name + } return ( diff --git a/manifests/base/mlx-deployments/mlx-api.yaml b/manifests/base/mlx-deployments/mlx-api.yaml index 77884a73..31475ecc 100644 --- a/manifests/base/mlx-deployments/mlx-api.yaml +++ b/manifests/base/mlx-deployments/mlx-api.yaml @@ -97,6 +97,9 @@ rules: - apiGroups: ["serving.knative.dev"] resources: ["services", "revisions", "configurations"] verbs: ["get", "list", "create", "update", "delete", "patch", "watch"] +- apiGroups: ["serving.kserve.io"] + resources: ["predictors", "inferenceservices"] + verbs: ["get", "list", "create", "update", "delete", "patch", "watch"] --- apiVersion: v1 kind: ServiceAccount