Skip to content

Commit

Permalink
Fix NIM selection issue
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewballantyne committed Nov 15, 2024
1 parent 7ceb459 commit 31eb80d
Show file tree
Hide file tree
Showing 11 changed files with 150 additions and 92 deletions.
1 change: 1 addition & 0 deletions frontend/src/concepts/areas/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,5 +122,6 @@ export const SupportedAreasStateMap: SupportedAreasState = {
},
[SupportedArea.NIM_MODEL]: {
featureFlags: ['disableNIMModelServing'],
reliantAreas: [SupportedArea.K_SERVE],
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,12 @@ import * as React from 'react';
import { Button } from '@patternfly/react-core';
import { useNavigate } from 'react-router-dom';
import { ModelServingContext } from '~/pages/modelServing/ModelServingContext';
import { getProjectModelServingPlatform } from '~/pages/modelServing/screens/projects/utils';
import { ServingRuntimePlatform } from '~/types';
import useServingPlatformStatuses from '~/pages/modelServing/useServingPlatformStatuses';
import EmptyDetailsView from '~/components/EmptyDetailsView';
import { ProjectObjectType, typedEmptyImage } from '~/concepts/design/utils';
import { ProjectSectionID } from '~/pages/projects/screens/detail/types';
import ServeModelButton from '~/pages/modelServing/screens/global/ServeModelButton';
import { getDisplayNameFromK8sResource } from '~/concepts/k8s/utils';
import { isProjectNIMSupported } from '~/pages/modelServing/screens/projects/nimUtils';

const EmptyModelServing: React.FC = () => {
const navigate = useNavigate();
Expand All @@ -19,14 +16,9 @@ const EmptyModelServing: React.FC = () => {
project,
} = React.useContext(ModelServingContext);
const servingPlatformStatuses = useServingPlatformStatuses();
const isKServeNIMEnabled = project ? isProjectNIMSupported(project) : false;

if (
(getProjectModelServingPlatform(project, servingPlatformStatuses).platform !==
ServingRuntimePlatform.SINGLE ||
isKServeNIMEnabled) &&
servingRuntimes.length === 0
) {
if (servingPlatformStatuses.modelMesh.enabled && servingRuntimes.length === 0) {
// Server needed -- must deploy from the project
return (
<EmptyDetailsView
title="No deployed models"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,9 @@ const ModelServingPlatform: React.FC = () => {
const deployingFromRegistry = !!(modelRegistryName && registeredModelId && modelVersionId);

const servingPlatformStatuses = useServingPlatformStatuses();
const {
kServe: { enabled: kServeEnabled },
modelMesh: { enabled: modelMeshEnabled },
nim: { available: isNIMAvailable },
numServingPlatformsAvailable,
} = servingPlatformStatuses;
const kServeEnabled = servingPlatformStatuses.kServe.enabled;
const isNIMAvailable = servingPlatformStatuses.kServeNIM.enabled;
const modelMeshEnabled = servingPlatformStatuses.modelMesh.enabled;

const {
servingRuntimes: {
Expand Down Expand Up @@ -96,12 +93,15 @@ const ModelServingPlatform: React.FC = () => {
const emptyTemplates = templatesEnabled.length === 0;
const emptyModelServer = servingRuntimes.length === 0;

const { platform: currentProjectServingPlatform, error: platformError } =
getProjectModelServingPlatform(currentProject, servingPlatformStatuses);
const {
platform: currentProjectServingPlatform,
hasMultiplePlatformOptions,
noPlatformsActive,
error: platformError,
} = getProjectModelServingPlatform(currentProject, servingPlatformStatuses);

const shouldShowPlatformSelection =
((kServeEnabled && modelMeshEnabled) || (!kServeEnabled && !modelMeshEnabled)) &&
!currentProjectServingPlatform;
(!!hasMultiplePlatformOptions || !!noPlatformsActive) && !currentProjectServingPlatform;

const isProjectModelMesh = currentProjectServingPlatform === ServingRuntimePlatform.MULTI;

Expand Down Expand Up @@ -259,7 +259,7 @@ const ModelServingPlatform: React.FC = () => {
isEmpty={shouldShowPlatformSelection}
loadError={platformError || servingRuntimeError || templateError}
emptyState={
kServeEnabled && modelMeshEnabled ? (
hasMultiplePlatformOptions ? (
<Flex alignItems={{ default: 'alignItemsCenter' }} gap={{ default: 'gapLg' }}>
<FlexItem
flex={{ default: 'flex_1' }}
Expand All @@ -281,16 +281,20 @@ const ModelServingPlatform: React.FC = () => {
</StackItem>
<StackItem>
<Gallery hasGutter>
<GalleryItem>
<EmptySingleModelServingCard
setErrorSelectingPlatform={setErrorSelectingPlatform}
/>
</GalleryItem>
<GalleryItem>
<EmptyMultiModelServingCard
setErrorSelectingPlatform={setErrorSelectingPlatform}
/>
</GalleryItem>
{kServeEnabled && (
<GalleryItem>
<EmptySingleModelServingCard
setErrorSelectingPlatform={setErrorSelectingPlatform}
/>
</GalleryItem>
)}
{modelMeshEnabled && (
<GalleryItem>
<EmptyMultiModelServingCard
setErrorSelectingPlatform={setErrorSelectingPlatform}
/>
</GalleryItem>
)}
{isNIMAvailable && (
<GalleryItem>
<EmptyNIMModelServingCard
Expand Down Expand Up @@ -330,7 +334,7 @@ const ModelServingPlatform: React.FC = () => {
? 'Multi-model serving enabled'
: 'Single-model serving enabled'}
</Label>
{emptyModelServer && numServingPlatformsAvailable > 1 && (
{emptyModelServer && hasMultiplePlatformOptions && (
<ModelServingPlatformSelectButton
namespace={currentProject.metadata.name}
servingPlatform={NamespaceApplicationCase.RESET_MODEL_SERVING_PLATFORM}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,21 +67,21 @@ const getMockServingPlatformStatuses = ({
kServeInstalled = true,
modelMeshEnabled = true,
modelMeshInstalled = true,
nimAvailable = true,
nimEnabled = false,
nimInstalled = false,
}): ServingPlatformStatuses => ({
kServe: {
enabled: kServeEnabled,
installed: kServeInstalled,
},
kServeNIM: {
enabled: nimEnabled,
installed: nimInstalled,
},
modelMesh: {
enabled: modelMeshEnabled,
installed: modelMeshInstalled,
},
nim: {
available: nimAvailable,
},
numServingPlatformsAvailable: [kServeEnabled, modelMeshEnabled, nimAvailable].filter(Boolean)
.length,
});

describe('getProjectModelServingPlatform', () => {
Expand All @@ -91,29 +91,45 @@ describe('getProjectModelServingPlatform', () => {
mockProjectK8sResource({}),
getMockServingPlatformStatuses({ kServeEnabled: false, modelMeshEnabled: false }),
),
).toStrictEqual({});
).toStrictEqual({
hasMultiplePlatformOptions: false,
noPlatformsActive: true,
});
});
it('should return undefined if both KServe and ModelMesh are enabled, and project has no platform label', () => {
expect(
getProjectModelServingPlatform(
mockProjectK8sResource({}),
getMockServingPlatformStatuses({}),
),
).toStrictEqual({});
).toStrictEqual({
hasMultiplePlatformOptions: true,
noPlatformsActive: false,
});
});
it('should return Single Platform if has platform label set to false and KServe is installed', () => {
expect(
getProjectModelServingPlatform(
mockProjectK8sResource({ enableModelMesh: false }),
getMockServingPlatformStatuses({}),
),
).toStrictEqual({ platform: ServingRuntimePlatform.SINGLE, error: undefined });
).toStrictEqual({
platform: ServingRuntimePlatform.SINGLE,
hasMultiplePlatformOptions: true,
noPlatformsActive: false,
error: undefined,
});
expect(
getProjectModelServingPlatform(
mockProjectK8sResource({ enableModelMesh: false }),
getMockServingPlatformStatuses({ kServeEnabled: false }),
),
).toStrictEqual({ platform: ServingRuntimePlatform.SINGLE, error: undefined });
).toStrictEqual({
platform: ServingRuntimePlatform.SINGLE,
hasMultiplePlatformOptions: false,
noPlatformsActive: false,
error: undefined,
});
});
it('should give error if has platform label set to false and KServe is not installed', () => {
expect(
Expand All @@ -129,13 +145,23 @@ describe('getProjectModelServingPlatform', () => {
mockProjectK8sResource({ enableModelMesh: true }),
getMockServingPlatformStatuses({}),
),
).toStrictEqual({ platform: ServingRuntimePlatform.MULTI, error: undefined });
).toStrictEqual({
platform: ServingRuntimePlatform.MULTI,
hasMultiplePlatformOptions: true,
noPlatformsActive: false,
error: undefined,
});
expect(
getProjectModelServingPlatform(
mockProjectK8sResource({ enableModelMesh: true }),
getMockServingPlatformStatuses({ modelMeshEnabled: false }),
),
).toStrictEqual({ platform: ServingRuntimePlatform.MULTI, error: undefined });
).toStrictEqual({
platform: ServingRuntimePlatform.MULTI,
hasMultiplePlatformOptions: false,
noPlatformsActive: false,
error: undefined,
});
});
it('should give error if has platform label set to true and ModelMesh is not installed', () => {
expect(
Expand All @@ -151,15 +177,23 @@ describe('getProjectModelServingPlatform', () => {
mockProjectK8sResource({}),
getMockServingPlatformStatuses({ modelMeshEnabled: false }),
),
).toStrictEqual({ platform: ServingRuntimePlatform.SINGLE });
).toStrictEqual({
platform: ServingRuntimePlatform.SINGLE,
hasMultiplePlatformOptions: false,
noPlatformsActive: false,
});
});
it('should return Multi Platform if only ModelMesh is enabled, and project has no platform label', () => {
expect(
getProjectModelServingPlatform(
mockProjectK8sResource({}),
getMockServingPlatformStatuses({ kServeEnabled: false }),
),
).toStrictEqual({ platform: ServingRuntimePlatform.MULTI });
).toStrictEqual({
platform: ServingRuntimePlatform.MULTI,
hasMultiplePlatformOptions: false,
noPlatformsActive: false,
});
});
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { useEffect, useState } from 'react';
import { SupportedArea, useIsAreaAvailable } from '~/concepts/areas';
import { isNIMServingRuntimeTemplateAvailable } from '~/pages/modelServing/screens/projects/nimUtils';
import { useDashboardNamespace } from '~/redux/selectors';

export const useIsNIMAvailable = (dashboardNamespace: string): boolean => {
export const useIsNIMAvailable = (): boolean => {
const { dashboardNamespace } = useDashboardNamespace();
const [isNIMAvailable, setIsNIMAvailable] = useState<boolean>(false);
const isNIMModelServingAvailable = useIsAreaAvailable(SupportedArea.NIM_MODEL).status;

Expand Down
43 changes: 36 additions & 7 deletions frontend/src/pages/modelServing/screens/projects/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,32 +295,61 @@ export const useCreateInferenceServiceObject = (
export const getProjectModelServingPlatform = (
project: ProjectKind | null,
platformStatuses: ServingPlatformStatuses,
): { platform?: ServingRuntimePlatform; error?: Error } => {
): {
platform?: ServingRuntimePlatform;
hasMultiplePlatformOptions?: boolean;
noPlatformsActive?: boolean;
error?: Error;
} => {
const {
kServe: { enabled: kServeEnabled, installed: kServeInstalled },
kServeNIM: { enabled: nimEnabled, installed: nimInstalled },

Check failure on line 306 in frontend/src/pages/modelServing/screens/projects/utils.ts

View workflow job for this annotation

GitHub Actions / Tests (18.x)

'nimInstalled' is assigned a value but never used
modelMesh: { enabled: modelMeshEnabled, installed: modelMeshInstalled },
} = platformStatuses;

if (!project) {
// Likely temporary or a bad usage of the hook
return {};
}

const allProjectsEnabledState = [kServeEnabled, modelMeshEnabled, nimEnabled];
const hasMultiplePlatformOptions = allProjectsEnabledState.filter(Boolean).length >= 2;
const noPlatformsActive = allProjectsEnabledState.filter(Boolean).length === 0;

const data: ReturnType<typeof getProjectModelServingPlatform> = {
hasMultiplePlatformOptions,
noPlatformsActive,
};

if (project.metadata.labels?.[KnownLabels.MODEL_SERVING_PROJECT] === undefined) {
if ((kServeEnabled && modelMeshEnabled) || (!kServeEnabled && !modelMeshEnabled)) {
return {};
// Auto-select logic
if (hasMultiplePlatformOptions || noPlatformsActive) {
return data;
}
if (modelMeshEnabled) {
return { platform: ServingRuntimePlatform.MULTI };
return { ...data, platform: ServingRuntimePlatform.MULTI };
}
if (kServeEnabled) {
return { platform: ServingRuntimePlatform.SINGLE };
return { ...data, platform: ServingRuntimePlatform.SINGLE };
}
}
if (project.metadata.labels?.[KnownLabels.MODEL_SERVING_PROJECT] === 'true') {
if (nimEnabled) {
// TODO: this is weird, it relies on KServe today... so it's never "only installed"
return { ...data, platform: ServingRuntimePlatform.SINGLE };
}

// TODO: unreachable code unless adding a new platform? probably should throw an error
} else if (project.metadata.labels[KnownLabels.MODEL_SERVING_PROJECT] === 'true') {
// Model mesh logic
return {
...data,
platform: ServingRuntimePlatform.MULTI,
error: modelMeshInstalled ? undefined : new Error('Multi-model platform is not installed'),
};
}

// KServe logic
return {
...data,
platform: ServingRuntimePlatform.SINGLE,
error: kServeInstalled ? undefined : new Error('Single-model platform is not installed'),
};
Expand Down
19 changes: 7 additions & 12 deletions frontend/src/pages/modelServing/screens/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,19 +108,14 @@ export type ServingRuntimeEditInfo = {
secrets: SecretKind[];
};

type PlatformStatus = {
enabled: boolean;
installed: boolean;
};
export type ServingPlatformStatuses = {
kServe: {
enabled: boolean;
installed: boolean;
};
modelMesh: {
enabled: boolean;
installed: boolean;
};
nim: {
available: boolean;
};
numServingPlatformsAvailable: number;
kServe: PlatformStatus;
kServeNIM: PlatformStatus;
modelMesh: PlatformStatus;
};

export type LabeledDataConnection = {
Expand Down
Loading

0 comments on commit 31eb80d

Please sign in to comment.