diff --git a/frontend/src/__mocks__/index.ts b/frontend/src/__mocks__/index.ts index b73f92d395..22f4990248 100644 --- a/frontend/src/__mocks__/index.ts +++ b/frontend/src/__mocks__/index.ts @@ -15,3 +15,11 @@ export * from './mockPipelineKF'; export * from './mockSecretK8sResource'; export * from './mockGoogleRpcStatusKF'; export * from './mockK8sStatus'; +export * from './mockComponents'; +export * from './mockRegisteredModel'; +export * from './mockModelVersion'; +export * from './mockModelVersionList'; +export * from './mockModelArtifactList'; +export * from './mockModelRegistryService'; +export * from './mockServingRuntimeK8sResource'; +export * from './mockInferenceServiceK8sResource'; diff --git a/frontend/src/__tests__/cypress/cypress/pages/modelRegistry/modelVersionDetails.ts b/frontend/src/__tests__/cypress/cypress/pages/modelRegistry/modelVersionDetails.ts index 4b096b245e..39dd95c138 100644 --- a/frontend/src/__tests__/cypress/cypress/pages/modelRegistry/modelVersionDetails.ts +++ b/frontend/src/__tests__/cypress/cypress/pages/modelRegistry/modelVersionDetails.ts @@ -46,6 +46,14 @@ class ModelVersionDetails { findModelVersionDropdownItem(name: string) { return cy.findByTestId('model-version-selector-list').find('li').contains(name); } + + findDetailsTab() { + return cy.findByTestId('model-versions-details-tab'); + } + + findRegisteredDeploymentsTab() { + return cy.findByTestId('deployments-tab'); + } } export const modelVersionDetails = new ModelVersionDetails(); diff --git a/frontend/src/__tests__/cypress/cypress/tests/mocked/modelRegistry/modelVersionDetails.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/mocked/modelRegistry/modelVersionDetails.cy.ts index 0ecc098a86..5241d3c532 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/mocked/modelRegistry/modelVersionDetails.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/mocked/modelRegistry/modelVersionDetails.cy.ts @@ -1,14 +1,28 @@ /* eslint-disable camelcase */ -import { mockDashboardConfig, mockK8sResourceList } from '~/__mocks__'; -import { mockComponents } from '~/__mocks__/mockComponents'; -import { ServiceModel } from '~/__tests__/cypress/cypress/utils/models'; -import { modelVersionDetails } from '~/__tests__/cypress/cypress/pages/modelRegistry/modelVersionDetails'; -import { mockRegisteredModel } from '~/__mocks__/mockRegisteredModel'; -import { mockModelVersion } from '~/__mocks__/mockModelVersion'; -import { mockModelVersionList } from '~/__mocks__/mockModelVersionList'; -import { mockModelArtifactList } from '~/__mocks__/mockModelArtifactList'; +import { + mockDashboardConfig, + mockK8sResourceList, + mockComponents, + mockRegisteredModel, + mockModelVersion, + mockModelVersionList, + mockModelArtifactList, + mockModelRegistryService, + mockServingRuntimeK8sResource, + mockInferenceServiceK8sResource, + mockProjectK8sResource, +} from '~/__mocks__'; + +import { + InferenceServiceModel, + ProjectModel, + ServiceModel, + ServingRuntimeModel, +} from '~/__tests__/cypress/cypress/utils/models'; import { verifyRelativeURL } from '~/__tests__/cypress/cypress/utils/url'; -import { mockModelRegistryService } from '~/__mocks__/mockModelRegistryService'; +import { modelVersionDetails } from '~/__tests__/cypress/cypress/pages/modelRegistry/modelVersionDetails'; +import { InferenceServiceModelState } from '~/pages/modelServing/screens/types'; +import { modelServingGlobal } from '~/__tests__/cypress/cypress/pages/modelServing'; const MODEL_REGISTRY_API_VERSION = 'v1alpha3'; @@ -29,6 +43,8 @@ const initIntercepts = () => { ]), ); + cy.interceptK8sList(ProjectModel, mockK8sResourceList([mockProjectK8sResource({})])); + cy.interceptOdh( `GET /api/service/modelregistry/:serviceName/api/model_registry/:apiVersion/registered_models/:registeredModelId`, { @@ -116,41 +132,82 @@ const initIntercepts = () => { }; describe('Model version details', () => { - beforeEach(() => { - initIntercepts(); - modelVersionDetails.visit(); - }); + describe('Details tab', () => { + beforeEach(() => { + initIntercepts(); + modelVersionDetails.visit(); + }); - it('Model version details page header', () => { - verifyRelativeURL('/modelRegistry/modelregistry-sample/registeredModels/1/versions/1/details'); - cy.findByTestId('app-page-title').should('have.text', 'Version 1'); - cy.findByTestId('breadcrumb-version-name').should('have.text', 'Version 1'); - }); + it('Model version details page header', () => { + verifyRelativeURL( + '/modelRegistry/modelregistry-sample/registeredModels/1/versions/1/details', + ); + cy.findByTestId('app-page-title').should('have.text', 'Version 1'); + cy.findByTestId('breadcrumb-version-name').should('have.text', 'Version 1'); + }); - it('Model version details tab', () => { - modelVersionDetails.findVersionId().contains('1'); - modelVersionDetails.findDescription().should('have.text', 'Description of model version'); - modelVersionDetails.findMoreLabelsButton().contains('6 more'); - modelVersionDetails.findMoreLabelsButton().click(); - modelVersionDetails.shouldContainsModalLabels([ - 'Testing label', - 'Financial', - 'Financial data', - 'Fraud detection', - 'Machine learning', - 'Next data to be overflow', - 'Label x', - 'Label y', - 'Label z', - ]); - modelVersionDetails.findStorageLocation().contains('s3://test-bucket/demo-models/test-path'); + it('Model version details tab', () => { + modelVersionDetails.findVersionId().contains('1'); + modelVersionDetails.findDescription().should('have.text', 'Description of model version'); + modelVersionDetails.findMoreLabelsButton().contains('6 more'); + modelVersionDetails.findMoreLabelsButton().click(); + modelVersionDetails.shouldContainsModalLabels([ + 'Testing label', + 'Financial', + 'Financial data', + 'Fraud detection', + 'Machine learning', + 'Next data to be overflow', + 'Label x', + 'Label y', + 'Label z', + ]); + modelVersionDetails.findStorageLocation().contains('s3://test-bucket/demo-models/test-path'); + }); + + it('Switching model versions', () => { + modelVersionDetails.findVersionId().contains('1'); + modelVersionDetails.findModelVersionDropdownButton().click(); + modelVersionDetails.findModelVersionDropdownSearch().fill('Version 2'); + modelVersionDetails.findModelVersionDropdownItem('Version 2').click(); + modelVersionDetails.findVersionId().contains('2'); + }); }); - it('Switching model versions', () => { - modelVersionDetails.findVersionId().contains('1'); - modelVersionDetails.findModelVersionDropdownButton().click(); - modelVersionDetails.findModelVersionDropdownSearch().fill('Version 2'); - modelVersionDetails.findModelVersionDropdownItem('Version 2').click(); - modelVersionDetails.findVersionId().contains('2'); + describe('Registered deployments tab', () => { + beforeEach(() => { + initIntercepts(); + }); + + it('renders empty state when the version has no registered deployments', () => { + cy.interceptK8sList(InferenceServiceModel, mockK8sResourceList([])); + cy.interceptK8sList(ServingRuntimeModel, mockK8sResourceList([])); + + modelVersionDetails.visit(); + modelVersionDetails.findRegisteredDeploymentsTab().click(); + + cy.findByTestId('model-version-deployments-empty-state').should('exist'); + }); + + it('renders table with data', () => { + cy.interceptK8sList( + InferenceServiceModel, + mockK8sResourceList([ + mockInferenceServiceK8sResource({ + url: 'test-inference-status.url.com', + activeModelState: InferenceServiceModelState.LOADED, + }), + ]), + ); + cy.interceptK8sList( + ServingRuntimeModel, + mockK8sResourceList([mockServingRuntimeK8sResource({})]), + ); + + modelVersionDetails.visit(); + modelVersionDetails.findRegisteredDeploymentsTab().click(); + + modelServingGlobal.getModelRow('Test Inference Service').should('exist'); + }); }); }); diff --git a/frontend/src/api/k8s/inferenceServices.ts b/frontend/src/api/k8s/inferenceServices.ts index 0e64b7656a..b4512d4fac 100644 --- a/frontend/src/api/k8s/inferenceServices.ts +++ b/frontend/src/api/k8s/inferenceServices.ts @@ -90,6 +90,7 @@ export const assembleInferenceService = ( [KnownLabels.DASHBOARD_RESOURCE]: 'true', ...(!isModelMesh && !externalRoute && { 'networking.knative.dev/visibility': 'cluster-local' }), + ...data.labels, }, annotations: { 'openshift.io/display-name': data.name.trim(), diff --git a/frontend/src/k8sTypes.ts b/frontend/src/k8sTypes.ts index 5167f7027b..47afa85081 100644 --- a/frontend/src/k8sTypes.ts +++ b/frontend/src/k8sTypes.ts @@ -24,6 +24,8 @@ export enum KnownLabels { DATA_CONNECTION_AWS = 'opendatahub.io/managed', LABEL_SELECTOR_MODEL_REGISTRY = 'component=model-registry', PROJECT_SUBJECT = 'opendatahub.io/rb-project-subject', + REGISTERED_MODEL_ID = 'modelregistry.opendatahub.io/registered-model-id', + MODEL_VERSION_ID = 'modelregistry.opendatahub.io/model-version-id', } export type K8sVerb = diff --git a/frontend/src/pages/modelRegistry/screens/ModelVersionDetails/ModelVersionDetails.tsx b/frontend/src/pages/modelRegistry/screens/ModelVersionDetails/ModelVersionDetails.tsx index 82b24e9239..26b3f6c2a1 100644 --- a/frontend/src/pages/modelRegistry/screens/ModelVersionDetails/ModelVersionDetails.tsx +++ b/frontend/src/pages/modelRegistry/screens/ModelVersionDetails/ModelVersionDetails.tsx @@ -7,6 +7,9 @@ import useModelVersionById from '~/concepts/modelRegistry/apiHooks/useModelVersi import { ModelRegistrySelectorContext } from '~/concepts/modelRegistry/context/ModelRegistrySelectorContext'; import { modelVersionUrl, registeredModelUrl } from '~/pages/modelRegistry/screens/routeUtils'; import useRegisteredModelById from '~/concepts/modelRegistry/apiHooks/useRegisteredModelById'; +import useInferenceServices from '~/pages/modelServing/useInferenceServices'; +import useServingRuntimes from '~/pages/modelServing/useServingRuntimes'; +import { useMakeFetchObject } from '~/utilities/useMakeFetchObject'; import { ModelVersionDetailsTab } from './const'; import ModelVersionsDetailsHeaderActions from './ModelVersionDetailsHeaderActions'; import ModelVersionDetailsTabs from './ModelVersionDetailsTabs'; @@ -26,7 +29,17 @@ const ModelVersionsDetails: React.FC = ({ tab, ...page const { modelVersionId: mvId, registeredModelId: rmId } = useParams(); const [rm] = useRegisteredModelById(rmId); - const [mv, mvLoaded, mvLoadError, refresh] = useModelVersionById(mvId); + const [mv, mvLoaded, mvLoadError, refreshModelVersion] = useModelVersionById(mvId); + const inferenceServices = useMakeFetchObject( + useInferenceServices(undefined, mv?.registeredModelId, mv?.id), + ); + const servingRuntimes = useMakeFetchObject(useServingRuntimes()); + + const refresh = React.useCallback(() => { + refreshModelVersion(); + inferenceServices.refresh(); + servingRuntimes.refresh(); + }, [inferenceServices, servingRuntimes, refreshModelVersion]); return ( = ({ tab, ...page /> - + ) @@ -82,7 +95,15 @@ const ModelVersionsDetails: React.FC = ({ tab, ...page loaded={mvLoaded} provideChildrenPadding > - {mv !== null && } + {mv !== null && ( + + )} ); }; diff --git a/frontend/src/pages/modelRegistry/screens/ModelVersionDetails/ModelVersionDetailsHeaderActions.tsx b/frontend/src/pages/modelRegistry/screens/ModelVersionDetails/ModelVersionDetailsHeaderActions.tsx index 59f01cf536..b8b41d4690 100644 --- a/frontend/src/pages/modelRegistry/screens/ModelVersionDetails/ModelVersionDetailsHeaderActions.tsx +++ b/frontend/src/pages/modelRegistry/screens/ModelVersionDetails/ModelVersionDetailsHeaderActions.tsx @@ -6,15 +6,20 @@ import { ModelRegistryContext } from '~/concepts/modelRegistry/context/ModelRegi import { ModelVersion, ModelState } from '~/concepts/modelRegistry/types'; import { getPatchBodyForModelVersion } from '~/pages/modelRegistry/screens/utils'; import { ModelRegistrySelectorContext } from '~/concepts/modelRegistry/context/ModelRegistrySelectorContext'; -import { modelVersionArchiveDetailsUrl } from '~/pages/modelRegistry/screens/routeUtils'; +import { + modelVersionArchiveDetailsUrl, + modelVersionDeploymentsUrl, +} from '~/pages/modelRegistry/screens/routeUtils'; import DeployRegisteredModelModal from '~/pages/modelRegistry/screens/components/DeployRegisteredModelModal'; interface ModelVersionsDetailsHeaderActionsProps { mv: ModelVersion; + refresh: () => void; } const ModelVersionsDetailsHeaderActions: React.FC = ({ mv, + refresh, }) => { const { apiState } = React.useContext(ModelRegistryContext); const { preferredModelRegistry } = React.useContext(ModelRegistrySelectorContext); @@ -67,6 +72,16 @@ const ModelVersionsDetailsHeaderActions: React.FC { + refresh(); + navigate( + modelVersionDeploymentsUrl( + mv.id, + mv.registeredModelId, + preferredModelRegistry?.metadata.name, + ), + ); + }} onCancel={() => setIsDeployModalOpen(false)} isOpen={isDeployModalOpen} modelVersion={mv} diff --git a/frontend/src/pages/modelRegistry/screens/ModelVersionDetails/ModelVersionDetailsTabs.tsx b/frontend/src/pages/modelRegistry/screens/ModelVersionDetails/ModelVersionDetailsTabs.tsx index 1bd554c2a5..54deea14dc 100644 --- a/frontend/src/pages/modelRegistry/screens/ModelVersionDetails/ModelVersionDetailsTabs.tsx +++ b/frontend/src/pages/modelRegistry/screens/ModelVersionDetails/ModelVersionDetailsTabs.tsx @@ -3,6 +3,8 @@ import { useNavigate } from 'react-router-dom'; import { PageSection, Tab, Tabs, TabTitleText } from '@patternfly/react-core'; import '~/pages/pipelines/global/runs/GlobalPipelineRunsTabs.scss'; import { ModelVersion } from '~/concepts/modelRegistry/types'; +import { InferenceServiceKind, ServingRuntimeKind } from '~/k8sTypes'; +import { FetchStateObject } from '~/types'; import { ModelVersionDetailsTabTitle, ModelVersionDetailsTab } from './const'; import ModelVersionDetailsView from './ModelVersionDetailsView'; import ModelVersionRegisteredDeploymentsView from './ModelVersionRegisteredDeploymentsView'; @@ -10,12 +12,16 @@ import ModelVersionRegisteredDeploymentsView from './ModelVersionRegisteredDeplo type ModelVersionDetailTabsProps = { tab: ModelVersionDetailsTab; modelVersion: ModelVersion; + inferenceServices: FetchStateObject; + servingRuntimes: FetchStateObject; refresh: () => void; }; const ModelVersionDetailsTabs: React.FC = ({ tab, modelVersion: mv, + inferenceServices, + servingRuntimes, refresh, }) => { const navigate = useNavigate(); @@ -44,7 +50,10 @@ const ModelVersionDetailsTabs: React.FC = ({ data-testid="deployments-tab" > - + diff --git a/frontend/src/pages/modelRegistry/screens/ModelVersionDetails/ModelVersionRegisteredDeploymentsView.tsx b/frontend/src/pages/modelRegistry/screens/ModelVersionDetails/ModelVersionRegisteredDeploymentsView.tsx index 7cc8baee48..a050e1535f 100644 --- a/frontend/src/pages/modelRegistry/screens/ModelVersionDetails/ModelVersionRegisteredDeploymentsView.tsx +++ b/frontend/src/pages/modelRegistry/screens/ModelVersionDetails/ModelVersionRegisteredDeploymentsView.tsx @@ -1,22 +1,49 @@ -import * as React from 'react'; -import { ModelVersion } from '~/concepts/modelRegistry/types'; +import React from 'react'; +import { Link } from 'react-router-dom'; + +import { Alert, Stack } from '@patternfly/react-core'; + import EmptyModelRegistryState from '~/pages/modelRegistry/screens/components/EmptyModelRegistryState'; +import InferenceServiceTable from '~/pages/modelServing/screens/global/InferenceServiceTable'; +import { getVersionDetailsInferenceServiceColumns } from '~/pages/modelServing/screens/global/data'; +import ModelVersionDetailsTabs from './ModelVersionDetailsTabs'; -type ModelVersionRegisteredDeploymentsViewProps = { - modelVersion: ModelVersion; -}; +type ModelVersionRegisteredDeploymentsViewProps = Pick< + React.ComponentProps, + 'inferenceServices' | 'servingRuntimes' +>; const ModelVersionRegisteredDeploymentsView: React.FC< ModelVersionRegisteredDeploymentsViewProps -> = ({ modelVersion: mv }) => { - // eslint-disable-next-line no-console - console.log({ mv }); - //TODO: implement component +> = ({ inferenceServices, servingRuntimes }) => { + const isLoading = !inferenceServices.loaded || !servingRuntimes.loaded; + + if (!isLoading && !inferenceServices.data.length) { + return ( + + ); + } + return ( - + + + This list includes only deployments that were initiated from the model registry. To view and + manage all of your deployments, go to the Model Serving{' '} + page. + + + getVersionDetailsInferenceServiceColumns(projects)} + inferenceServices={inferenceServices.data} + servingRuntimes={servingRuntimes.data} + isLoading={isLoading} + /> + ); }; export default ModelVersionRegisteredDeploymentsView; diff --git a/frontend/src/pages/modelRegistry/screens/ModelVersions/ModelVersionsTableRow.tsx b/frontend/src/pages/modelRegistry/screens/ModelVersions/ModelVersionsTableRow.tsx index d0f69a9b75..8a1eb60916 100644 --- a/frontend/src/pages/modelRegistry/screens/ModelVersions/ModelVersionsTableRow.tsx +++ b/frontend/src/pages/modelRegistry/screens/ModelVersions/ModelVersionsTableRow.tsx @@ -8,6 +8,7 @@ import ModelLabels from '~/pages/modelRegistry/screens/components/ModelLabels'; import ModelTimestamp from '~/pages/modelRegistry/screens/components/ModelTimestamp'; import { modelVersionArchiveDetailsUrl, + modelVersionDeploymentsUrl, modelVersionUrl, } from '~/pages/modelRegistry/screens/routeUtils'; import { ArchiveModelVersionModal } from '~/pages/modelRegistry/screens/components/ArchiveModelVersionModal'; @@ -107,6 +108,15 @@ const ModelVersionsTableRow: React.FC = ({ modelVersionName={mv.name} /> + navigate( + modelVersionDeploymentsUrl( + mv.id, + mv.registeredModelId, + preferredModelRegistry?.metadata.name, + ), + ) + } onCancel={() => setIsDeployModalOpen(false)} isOpen={isDeployModalOpen} modelVersion={mv} diff --git a/frontend/src/pages/modelRegistry/screens/ModelVersionsArchive/ModelVersionArchiveDetails.tsx b/frontend/src/pages/modelRegistry/screens/ModelVersionsArchive/ModelVersionArchiveDetails.tsx index 4abe969278..970675b24b 100644 --- a/frontend/src/pages/modelRegistry/screens/ModelVersionsArchive/ModelVersionArchiveDetails.tsx +++ b/frontend/src/pages/modelRegistry/screens/ModelVersionsArchive/ModelVersionArchiveDetails.tsx @@ -12,6 +12,9 @@ import { RestoreModelVersionModal } from '~/pages/modelRegistry/screens/componen import { ModelRegistryContext } from '~/concepts/modelRegistry/context/ModelRegistryContext'; import { getPatchBodyForModelVersion } from '~/pages/modelRegistry/screens/utils'; import { ModelState } from '~/concepts/modelRegistry/types'; +import useInferenceServices from '~/pages/modelServing/useInferenceServices'; +import useServingRuntimes from '~/pages/modelServing/useServingRuntimes'; +import { useMakeFetchObject } from '~/utilities/useMakeFetchObject'; import ModelVersionArchiveDetailsBreadcrumb from './ModelVersionArchiveDetailsBreadcrumb'; type ModelVersionsArchiveDetailsProps = { @@ -32,8 +35,12 @@ const ModelVersionsArchiveDetails: React.FC = const { modelVersionId: mvId, registeredModelId: rmId } = useParams(); const [rm] = useRegisteredModelById(rmId); - const [mv, mvLoaded, mvLoadError, refresh] = useModelVersionById(mvId); + const [mv, mvLoaded, mvLoadError, refreshModelVersion] = useModelVersionById(mvId); const [isRestoreModalOpen, setIsRestoreModalOpen] = React.useState(false); + const inferenceServices = useMakeFetchObject( + useInferenceServices(undefined, mv?.registeredModelId, mv?.id), + ); + const servingRuntimes = useMakeFetchObject(useServingRuntimes()); return ( <> @@ -68,7 +75,15 @@ const ModelVersionsArchiveDetails: React.FC = loaded={mvLoaded} provideChildrenPadding > - {mv !== null && } + {mv !== null && ( + + )} {mv !== null && ( void; isOpen: boolean; modelVersion: ModelVersion; + onCancel: () => void; + onSubmit?: () => void; } const DeployRegisteredModelModal: React.FC = ({ isOpen, - onCancel, modelVersion, + onCancel, + onSubmit, }) => { const { servingRuntimeTemplates: [templates], @@ -48,10 +50,17 @@ const DeployRegisteredModelModal: React.FC = ({ error: deployInfoError, } = useRegisteredModelDeployInfo(modelVersion); - const onClose = React.useCallback(() => { - setSelectedProject(null); - onCancel(); - }, [onCancel]); + const onClose = React.useCallback( + (submit: boolean) => { + if (submit) { + onSubmit?.(); + } + + setSelectedProject(null); + onCancel(); + }, + [onCancel, onSubmit], + ); if ( (platform === ServingRuntimePlatform.MULTI && !projectDeployStatusLoaded) || @@ -64,12 +73,12 @@ const DeployRegisteredModelModal: React.FC = ({ description="Configure properties for deploying your model" variant="medium" isOpen={isOpen} - onClose={onClose} + onClose={() => onClose(false)} actions={[ , - , ]} diff --git a/frontend/src/pages/modelRegistry/screens/routeUtils.ts b/frontend/src/pages/modelRegistry/screens/routeUtils.ts index 1c46314f00..e7ec95efe8 100644 --- a/frontend/src/pages/modelRegistry/screens/routeUtils.ts +++ b/frontend/src/pages/modelRegistry/screens/routeUtils.ts @@ -43,3 +43,9 @@ export const registerVersionForModelUrl = ( rmId?: string, preferredModelRegistry?: string, ): string => `${registeredModelUrl(rmId, preferredModelRegistry)}/registerVersion`; + +export const modelVersionDeploymentsUrl = ( + mvId: string, + rmId?: string, + preferredModelRegistry?: string, +): string => `${modelVersionUrl(mvId, rmId, preferredModelRegistry)}/deployments`; diff --git a/frontend/src/pages/modelServing/__tests__/useInferenceServices.spec.ts b/frontend/src/pages/modelServing/__tests__/useInferenceServices.spec.ts index 51c873990b..12e8a46253 100644 --- a/frontend/src/pages/modelServing/__tests__/useInferenceServices.spec.ts +++ b/frontend/src/pages/modelServing/__tests__/useInferenceServices.spec.ts @@ -184,6 +184,7 @@ describe('useInferenceServices', () => { expect(renderResult).hookToHaveUpdateCount(3); expect(renderResult).hookToBeStable([false, true, true, true]); }); + it('should fail to fetch InferenceService', async () => { useModelServingEnabledMock.mockReturnValue(true); k8sListResourceMock.mockRejectedValue(new Error('error')); @@ -198,4 +199,55 @@ describe('useInferenceServices', () => { expect(renderResult).hookToHaveUpdateCount(2); expect(renderResult).hookToBeStable([false, true, false, true]); }); + + it('has "dashboard" labelSelector by default', async () => { + useModelServingEnabledMock.mockReturnValue(true); + useAccessReviewMock.mockReturnValue([true, true]); + k8sListResourceMock.mockResolvedValue(mockInferenceServices); + + testHook(useInferenceServices)(); + + expect(k8sListResourceMock.mock.calls[0][0].queryOptions?.queryParams).toEqual({ + labelSelector: 'opendatahub.io/dashboard=true', + }); + }); + + it('includes "registered-model-id" labelSelector when "registeredModelId" is specified', async () => { + useModelServingEnabledMock.mockReturnValue(true); + useAccessReviewMock.mockReturnValue([true, true]); + k8sListResourceMock.mockResolvedValue(mockInferenceServices); + + testHook(useInferenceServices)(undefined, 'some-registered-model-id'); + + expect(k8sListResourceMock.mock.calls[0][0].queryOptions?.queryParams).toEqual({ + labelSelector: + 'opendatahub.io/dashboard=true,modelregistry.opendatahub.io/registered-model-id=some-registered-model-id', + }); + }); + + it('includes "model-version-id" labelSelector when "modelVersionId" is specified', async () => { + useModelServingEnabledMock.mockReturnValue(true); + useAccessReviewMock.mockReturnValue([true, true]); + k8sListResourceMock.mockResolvedValue(mockInferenceServices); + + testHook(useInferenceServices)(undefined, undefined, 'some-model-version-id'); + + expect(k8sListResourceMock.mock.calls[0][0].queryOptions?.queryParams).toEqual({ + labelSelector: + 'opendatahub.io/dashboard=true,modelregistry.opendatahub.io/model-version-id=some-model-version-id', + }); + }); + + it('includes appropriate labelSelectors when "modelVersionId" and "registeredModelId" are specified', async () => { + useModelServingEnabledMock.mockReturnValue(true); + useAccessReviewMock.mockReturnValue([true, true]); + k8sListResourceMock.mockResolvedValue(mockInferenceServices); + + testHook(useInferenceServices)(undefined, 'some-registered-model-id', 'some-model-version-id'); + + expect(k8sListResourceMock.mock.calls[0][0].queryOptions?.queryParams).toEqual({ + labelSelector: + 'opendatahub.io/dashboard=true,modelregistry.opendatahub.io/registered-model-id=some-registered-model-id,modelregistry.opendatahub.io/model-version-id=some-model-version-id', + }); + }); }); diff --git a/frontend/src/pages/modelServing/screens/global/InferenceServiceListView.tsx b/frontend/src/pages/modelServing/screens/global/InferenceServiceListView.tsx index e1d7a6fd5f..5e6c6dd1d2 100644 --- a/frontend/src/pages/modelServing/screens/global/InferenceServiceListView.tsx +++ b/frontend/src/pages/modelServing/screens/global/InferenceServiceListView.tsx @@ -50,6 +50,7 @@ const InferenceServiceListView: React.FC = ({ return ( = ({ inferenceService }) => { +const InferenceServiceProject: React.FC = ({ + inferenceService, + isCompact, +}) => { const { modelServingProjects, loaded, loadError } = React.useContext(ProjectsContext); if (!loaded) { @@ -32,7 +36,7 @@ const InferenceServiceProject: React.FC = ({ infer {project ? ( <> {getDisplayNameFromK8sResource(project)}{' '} -