Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RHOAIENG-6985] Create kserve metrics feature flag #2795

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions backend/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export type DashboardConfig = K8sResourceCommon & {
disablePerformanceMetrics: boolean;
disableKServe: boolean;
disableKServeAuth: boolean;
disableKServeMetrics: boolean;
disableModelMesh: boolean;
disableAcceleratorProfiles: boolean;
disablePipelineExperiments: boolean;
Expand Down
1 change: 1 addition & 0 deletions backend/src/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export const blankDashboardCR: DashboardConfig = {
disablePipelines: false,
disableKServe: false,
disableKServeAuth: false,
disableKServeMetrics: true,
disableModelMesh: false,
disableAcceleratorProfiles: false,
disablePipelineExperiments: true,
Expand Down
3 changes: 3 additions & 0 deletions docs/dashboard-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ The following are a list of features that are supported, along with there defaul
| disableCustomServingRuntimes | false | Disables Custom Serving Runtimes from the Admin Panel. |
| disableKServe | false | Disables the ability to select KServe as a Serving Platform. |
| disableKServeAuth | false | Disables the ability to use auth in KServe. |
| disableKServeMetrics | true | Disables the ability to see KServe Metrics. |
| disableModelMesh | false | Disables the ability to select ModelMesh as a Serving Platform. |
| disableAcceleratorProfiles | false | Disables Accelerator profiles from the Admin Panel. |
| disableBiasMetrics | false | Disables Model Bias tab from Model Serving metrics. |
Expand Down Expand Up @@ -57,6 +58,7 @@ spec:
disableProjectSharing: false
disableCustomServingRuntimes: false
disableAcceleratorProfiles: false
disableKServeMetrics: true
disableBiasMetrics: false
disablePerformanceMetrics: false
disablePipelineExperiments: false
Expand Down Expand Up @@ -151,6 +153,7 @@ spec:
disableProjectSharing: true
disableCustomServingRuntimes: false
disableAcceleratorProfiles: true
disableKServeMetrics: true
disableBiasMetrics: false
disablePerformanceMetrics: false
disablePipelineExperiments: true
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/__mocks__/mockDashboardConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type MockDashboardConfigType = {
disableCustomServingRuntimes?: boolean;
disableKServe?: boolean;
disableKServeAuth?: boolean;
disableKServeMetrics?: boolean;
disableModelMesh?: boolean;
disableAcceleratorProfiles?: boolean;
disablePerformanceMetrics?: boolean;
Expand All @@ -42,6 +43,7 @@ export const mockDashboardConfig = ({
disablePipelines = false,
disableKServe = false,
disableKServeAuth = false,
disableKServeMetrics = true,
disableModelMesh = false,
disableAcceleratorProfiles = false,
disablePerformanceMetrics = false,
Expand Down Expand Up @@ -81,6 +83,7 @@ export const mockDashboardConfig = ({
disablePerformanceMetrics,
disableKServe,
disableKServeAuth,
disableKServeMetrics,
disableModelMesh,
disableAcceleratorProfiles,
disablePipelineExperiments,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type HandlersProps = {
inferenceServices?: InferenceServiceKind[];
delayInferenceServices?: boolean;
delayServingRuntimes?: boolean;
disableKServeMetrics?: boolean;
};

const initIntercepts = ({
Expand All @@ -46,6 +47,7 @@ const initIntercepts = ({
inferenceServices = [mockInferenceServiceK8sResource({})],
delayInferenceServices,
delayServingRuntimes,
disableKServeMetrics,
}: HandlersProps) => {
cy.interceptOdh(
'GET /api/dsc/status',
Expand All @@ -58,6 +60,7 @@ const initIntercepts = ({
mockDashboardConfig({
disableKServe: disableKServeConfig,
disableModelMesh: disableModelMeshConfig,
disableKServeMetrics,
}),
);
cy.interceptK8sList(ServingRuntimeModel, mockK8sResourceList(servingRuntimes));
Expand Down Expand Up @@ -478,6 +481,22 @@ describe('Model Serving Global', () => {
modelServingGlobal.findDeployModelButton().click();
cy.findByText('Error creating model server').should('not.exist');
});
it('Navigate to kserve model metrics page only if enabled', () => {
initIntercepts({});
modelServingGlobal.visit('test-project');

// Verify initial run rows exist
modelServingGlobal.getModelRow('Test Inference Service').should('have.length', 1);
modelServingGlobal.getModelMetricLink('Test Inference Service').should('not.exist');

initIntercepts({ disableKServeMetrics: false });
modelServingGlobal.visit('test-project');

modelServingGlobal.getModelRow('Test Inference Service').should('have.length', 1);
modelServingGlobal.getModelMetricLink('Test Inference Service').should('be.visible');
modelServingGlobal.getModelMetricLink('Test Inference Service').click();
cy.findByTestId('kserve-metrics-page').should('be.visible');
});

describe('Table filter and pagination', () => {
it('filter by name', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,17 @@ import {
SecretModel,
ServiceAccountModel,
TemplateModel,
InferenceServiceModel,
ServingRuntimeModel,
} from '~/__tests__/cypress/cypress/utils/models';
import { mockServingRuntimeK8sResource } from '~/__mocks__/mockServingRuntimeK8sResource';
import { mockInferenceServiceK8sResource } from '~/__mocks__/mockInferenceServiceK8sResource';

type HandlersProps = {
isEmpty?: boolean;
imageStreamName?: string;
disableKServeConfig?: boolean;
disableKServeMetrics?: boolean;
disableModelConfig?: boolean;
isEnabled?: string;
isUnknown?: boolean;
Expand All @@ -43,6 +48,7 @@ type HandlersProps = {

const initIntercepts = ({
disableKServeConfig,
disableKServeMetrics,
disableModelConfig,
isEmpty = false,
imageStreamName = 'test-image',
Expand Down Expand Up @@ -96,6 +102,7 @@ const initIntercepts = ({
mockDashboardConfig({
disableKServe: disableKServeConfig,
disableModelMesh: disableModelConfig,
disableKServeMetrics,
}),
);
if (pipelineServerInstalled) {
Expand Down Expand Up @@ -233,6 +240,13 @@ const initIntercepts = ({
};

describe('Project Details', () => {
const servingRuntimes = [mockServingRuntimeK8sResource({})];
const inferenceServices = [mockInferenceServiceK8sResource({})];
const initModelServingIntercepts = () => {
cy.interceptK8sList(ServingRuntimeModel, mockK8sResourceList(servingRuntimes));
cy.interceptK8sList(InferenceServiceModel, mockK8sResourceList(inferenceServices));
};

describe('Empty project details', () => {
it('Empty state component in project details', () => {
initIntercepts({ isEmpty: true });
Expand Down Expand Up @@ -271,6 +285,26 @@ describe('Project Details', () => {
projectDetails.findServingPlatformLabel().should('have.text', 'Single-model serving enabled');
});

it('Shows KServe metrics only when available', () => {
initIntercepts({ templates: true, disableKServeConfig: false, disableModelConfig: true });
initModelServingIntercepts();

projectDetails.visitSection('test-project', 'model-server');
projectDetails.getKserveModelMetricLink('Test Inference Service').should('not.exist');

initIntercepts({
templates: true,
disableKServeConfig: false,
disableModelConfig: true,
disableKServeMetrics: false,
});
initModelServingIntercepts();

projectDetails.visitSection('test-project', 'model-server');
projectDetails.getKserveModelMetricLink('Test Inference Service').should('be.visible');
projectDetails.getKserveModelMetricLink('Test Inference Service').click();
cy.findByTestId('kserve-metrics-page').should('be.visible');
});
it('Multi model serving platform is enabled', () => {
initIntercepts({ templates: true, disableKServeConfig: true, disableModelConfig: false });
projectDetails.visit('test-project');
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/__tests__/cypress/cypress/pages/modelServing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ class ModelServingGlobal {
return this.findModelsTable().find(`[data-label=Name]`).contains(name).parents('tr');
}

getModelMetricLink(name: string) {
return this.findModelsTable().findByTestId(`metrics-link-${name}`);
}

findEmptyResults() {
return cy.findByTestId('no-result-found-title');
}
Expand Down
8 changes: 8 additions & 0 deletions frontend/src/__tests__/cypress/cypress/pages/projects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,14 @@ class ProjectDetails {
findUnsupportedPipelineVersionAlert() {
return cy.findByTestId('unsupported-pipeline-version-alert');
}

private findKserveModelsTable() {
return cy.findByTestId('kserve-inference-service-table');
}

getKserveModelMetricLink(name: string) {
return this.findKserveModelsTable().findByTestId(`metrics-link-${name}`);
}
}

class ProjectDetailsSettingsTab extends ProjectDetails {
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/concepts/areas/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ export const SupportedAreasStateMap: SupportedAreasState = {
reliantAreas: [SupportedArea.K_SERVE],
requiredCapabilities: [StackCapability.SERVICE_MESH, StackCapability.SERVICE_MESH_AUTHZ],
},
[SupportedArea.K_SERVE_METRICS]: {
featureFlags: ['disableKServeMetrics'],
reliantAreas: [SupportedArea.K_SERVE, SupportedArea.MODEL_SERVING],
},
alexcreasy marked this conversation as resolved.
Show resolved Hide resolved
[SupportedArea.MODEL_MESH]: {
featureFlags: ['disableModelMesh'],
requiredComponents: [StackComponent.MODEL_MESH],
Expand All @@ -59,7 +63,6 @@ export const SupportedAreasStateMap: SupportedAreasState = {
},
[SupportedArea.PERFORMANCE_METRICS]: {
featureFlags: ['disablePerformanceMetrics'],
requiredComponents: [StackComponent.MODEL_MESH], // TODO: remove when KServe support is added
reliantAreas: [SupportedArea.MODEL_SERVING],
},
[SupportedArea.TRUSTY_AI]: {
Expand Down
1 change: 1 addition & 0 deletions frontend/src/concepts/areas/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export enum SupportedArea {
CUSTOM_RUNTIMES = 'custom-serving-runtimes',
K_SERVE = 'kserve',
K_SERVE_AUTH = 'kserve-auth',
K_SERVE_METRICS = 'kserve-metrics',
MODEL_MESH = 'model-mesh',
BIAS_METRICS = 'bias-metrics',
PERFORMANCE_METRICS = 'performance-metrics',
Expand Down
1 change: 1 addition & 0 deletions frontend/src/k8sTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1211,6 +1211,7 @@ export type DashboardCommonConfig = {
disablePerformanceMetrics: boolean;
disableKServe: boolean;
disableKServeAuth: boolean;
disableKServeMetrics: boolean;
disableModelMesh: boolean;
disableAcceleratorProfiles: boolean;
// TODO Temp feature flag - remove with https://issues.redhat.com/browse/RHOAIENG-3826
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import ResourceNameTooltip from '~/components/ResourceNameTooltip';
import useModelMetricsEnabled from '~/pages/modelServing/useModelMetricsEnabled';
import { InferenceServiceKind, ServingRuntimeKind } from '~/k8sTypes';
import { isModelMesh } from '~/pages/modelServing/utils';
import { SupportedArea } from '~/concepts/areas';
import useIsAreaAvailable from '~/concepts/areas/useIsAreaAvailable';
import { getInferenceServiceDisplayName } from './utils';
import InferenceServiceEndpoint from './InferenceServiceEndpoint';
import InferenceServiceProject from './InferenceServiceProject';
Expand All @@ -31,28 +33,42 @@ const InferenceServiceTableRow: React.FC<InferenceServiceTableRowProps> = ({
showServingRuntime,
}) => {
const [modelMetricsEnabled] = useModelMetricsEnabled();
const kserveMetricsEnabled = useIsAreaAvailable(SupportedArea.K_SERVE_METRICS).status;

const modelMetricsSupported = (modelMetricsInferenceService: InferenceServiceKind) =>
modelMetricsEnabled &&
modelMetricsInferenceService.metadata.annotations?.['serving.kserve.io/deploymentMode'] ===
'ModelMesh';
const modelMesh = isModelMesh(inferenceService);
const modelMeshMetricsSupported = modelMetricsEnabled && modelMesh;
const kserveMetricsSupported = modelMetricsEnabled && kserveMetricsEnabled && !modelMesh;

const displayName = getInferenceServiceDisplayName(inferenceService);

return (
<>
<Td dataLabel="Name">
<ResourceNameTooltip resource={inferenceService}>
{modelMetricsSupported(inferenceService) ? (
{modelMeshMetricsSupported ? (
<Link
data-testid={`metrics-link-${displayName}`}
to={
isGlobal
? `/modelServing/${inferenceService.metadata.namespace}/metrics/${inferenceService.metadata.name}`
: `/projects/${inferenceService.metadata.namespace}/metrics/model/${inferenceService.metadata.name}`
}
>
{getInferenceServiceDisplayName(inferenceService)}
{displayName}
</Link>
) : kserveMetricsSupported ? (
<Link
data-testid={`metrics-link-${displayName}`}
to={
isGlobal
? `/modelServing/${inferenceService.metadata.namespace}/metrics/${inferenceService.metadata.name}`
: `/projects/${inferenceService.metadata.namespace}/metrics/model/${inferenceService.metadata.name}`
}
>
{displayName}
</Link>
) : (
getInferenceServiceDisplayName(inferenceService)
displayName
)}
</ResourceNameTooltip>
</Td>
Expand All @@ -70,20 +86,17 @@ const InferenceServiceTableRow: React.FC<InferenceServiceTableRowProps> = ({
<InferenceServiceEndpoint
inferenceService={inferenceService}
servingRuntime={servingRuntime}
isKserve={!isModelMesh(inferenceService)}
isKserve={!modelMesh}
/>
</Td>
<Td dataLabel="API protocol">
<InferenceServiceAPIProtocol
servingRuntime={servingRuntime}
isMultiModel={modelMetricsSupported(inferenceService)}
isMultiModel={modelMeshMetricsSupported}
/>
</Td>
<Td dataLabel="Status">
<InferenceServiceStatus
inferenceService={inferenceService}
isKserve={!isModelMesh(inferenceService)}
/>
<InferenceServiceStatus inferenceService={inferenceService} isKserve={!modelMesh} />
</Td>
<Td isActionCell>
<ResourceActionsColumn
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const GlobalModelMetricsPage: React.FC = () => {
isActive: true,
},
]}
model={model}
type={PerformanceMetricType.MODEL}
/>
);
Expand Down
10 changes: 8 additions & 2 deletions frontend/src/pages/modelServing/screens/metrics/MetricsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,17 @@
import { PerformanceMetricType } from '~/pages/modelServing/screens/types';
import { TrustyAIContext } from '~/concepts/trustyai/context/TrustyAIContext';
import ServerMetricsPage from '~/pages/modelServing/screens/metrics/performance/ServerMetricsPage';
import { InferenceServiceKind } from '~/k8sTypes';
import { getBreadcrumbItemComponents } from './utils';

type MetricsPageProps = {
title: string;
breadcrumbItems: BreadcrumbItemType[];
type: PerformanceMetricType;
model?: InferenceServiceKind;
};

const MetricsPage: React.FC<MetricsPageProps> = ({ title, breadcrumbItems, type }) => {
const MetricsPage: React.FC<MetricsPageProps> = ({ title, breadcrumbItems, type, model }) => {
const { tab } = useParams();
const navigate = useNavigate();

Expand Down Expand Up @@ -46,7 +48,11 @@
)
}
>
{type === PerformanceMetricType.SERVER ? <ServerMetricsPage /> : <MetricsPageTabs />}
{type === PerformanceMetricType.SERVER ? (
<ServerMetricsPage />
) : model ? (
<MetricsPageTabs model={model} />
) : null}

Check warning on line 55 in frontend/src/pages/modelServing/screens/metrics/MetricsPage.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/modelServing/screens/metrics/MetricsPage.tsx#L55

Added line #L55 was not covered by tests
</ApplicationsPage>
);
};
Expand Down
Loading
Loading