From bc32c9d9256ff90587bd407cdc13ff02baf4d707 Mon Sep 17 00:00:00 2001 From: Jeffrey Phillips Date: Fri, 7 Jun 2024 08:45:24 -0400 Subject: [PATCH] [RHOAIENG-6522] Create model metrics kserve page --- .../modelServing/modelServingGlobal.cy.ts | 2 +- .../mocked/projects/projectDetails.cy.ts | 2 +- frontend/src/pages/UnknownError.tsx | 37 +++++++++++++++ .../metrics/EnsureMetricsAvailable.tsx | 15 +++++- .../metrics/performance/KserveMetrics.tsx | 22 +++++++++ .../performance/MetricsPlaceHolder.tsx | 32 +++++++++++++ .../metrics/performance/ModelGraphs.tsx | 44 ++++-------------- .../metrics/performance/ModelMeshMetrics.tsx | 46 +++++++++++++++++++ .../metrics/performance/PerformanceTab.tsx | 35 ++++++-------- 9 files changed, 175 insertions(+), 60 deletions(-) create mode 100644 frontend/src/pages/UnknownError.tsx create mode 100644 frontend/src/pages/modelServing/screens/metrics/performance/KserveMetrics.tsx create mode 100644 frontend/src/pages/modelServing/screens/metrics/performance/MetricsPlaceHolder.tsx create mode 100644 frontend/src/pages/modelServing/screens/metrics/performance/ModelMeshMetrics.tsx diff --git a/frontend/src/__tests__/cypress/cypress/tests/mocked/modelServing/modelServingGlobal.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/mocked/modelServing/modelServingGlobal.cy.ts index d507ff03e2..194d997c6d 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/mocked/modelServing/modelServingGlobal.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/mocked/modelServing/modelServingGlobal.cy.ts @@ -495,7 +495,7 @@ describe('Model Serving Global', () => { 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'); + cy.findByTestId('app-page-title').should('have.text', 'Test Inference Service metrics'); }); describe('Table filter and pagination', () => { diff --git a/frontend/src/__tests__/cypress/cypress/tests/mocked/projects/projectDetails.cy.ts b/frontend/src/__tests__/cypress/cypress/tests/mocked/projects/projectDetails.cy.ts index a7d9d9f2f4..2ee52b7425 100644 --- a/frontend/src/__tests__/cypress/cypress/tests/mocked/projects/projectDetails.cy.ts +++ b/frontend/src/__tests__/cypress/cypress/tests/mocked/projects/projectDetails.cy.ts @@ -305,7 +305,7 @@ describe('Project Details', () => { 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'); + cy.findByTestId('app-page-title').should('have.text', 'Test Inference Service metrics'); }); it('Multi model serving platform is enabled', () => { initIntercepts({ templates: true, disableKServeConfig: true, disableModelConfig: false }); diff --git a/frontend/src/pages/UnknownError.tsx b/frontend/src/pages/UnknownError.tsx new file mode 100644 index 0000000000..5d21cb6071 --- /dev/null +++ b/frontend/src/pages/UnknownError.tsx @@ -0,0 +1,37 @@ +import React from 'react'; +import { + EmptyState, + EmptyStateVariant, + EmptyStateIcon, + EmptyStateBody, + PageSection, + PageSectionVariants, + EmptyStateHeader, +} from '@patternfly/react-core'; +import { ErrorCircleOIcon } from '@patternfly/react-icons'; + +type UnauthorizedErrorProps = { + variant?: PageSectionVariants; + titleText: string; + error: Error; + testId?: string; +}; +const UnknownError: React.FC = ({ + variant = PageSectionVariants.default, + titleText, + error, + testId, +}) => ( + + + } + headingLevel="h5" + /> + {error.message} + + +); + +export default UnknownError; diff --git a/frontend/src/pages/modelServing/screens/metrics/EnsureMetricsAvailable.tsx b/frontend/src/pages/modelServing/screens/metrics/EnsureMetricsAvailable.tsx index ce103ca17f..85c1684d85 100644 --- a/frontend/src/pages/modelServing/screens/metrics/EnsureMetricsAvailable.tsx +++ b/frontend/src/pages/modelServing/screens/metrics/EnsureMetricsAvailable.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { Bullseye, PageSectionVariants, Spinner } from '@patternfly/react-core'; import { AxiosError } from 'axios'; import UnauthorizedError from '~/pages/UnauthorizedError'; +import UnknownError from '~/pages/UnknownError'; import { ModelMetricType, ModelServingMetricsContext, @@ -37,8 +38,18 @@ const EnsureMetricsAvailable: React.FC = ({ // Check for errors first as `loaded` prop will always be false when there is an error. If you check // for loaded first, you'll get an infinite spinner. - if (error?.response?.status === 403) { - return ; + if (error) { + if (error.response?.status === 403) { + return ; + } + return ( + + ); } if (readyCount !== metrics.length) { diff --git a/frontend/src/pages/modelServing/screens/metrics/performance/KserveMetrics.tsx b/frontend/src/pages/modelServing/screens/metrics/performance/KserveMetrics.tsx new file mode 100644 index 0000000000..7b3c698dd5 --- /dev/null +++ b/frontend/src/pages/modelServing/screens/metrics/performance/KserveMetrics.tsx @@ -0,0 +1,22 @@ +import * as React from 'react'; +import { Stack, StackItem } from '@patternfly/react-core'; +import MetricsPlaceHolder from '~/pages/modelServing/screens/metrics/performance/MetricsPlaceHolder'; + +const KserveMetrics: React.FC = () => ( + + + + + + + + + + + + + + +); + +export default KserveMetrics; diff --git a/frontend/src/pages/modelServing/screens/metrics/performance/MetricsPlaceHolder.tsx b/frontend/src/pages/modelServing/screens/metrics/performance/MetricsPlaceHolder.tsx new file mode 100644 index 0000000000..f7168176f8 --- /dev/null +++ b/frontend/src/pages/modelServing/screens/metrics/performance/MetricsPlaceHolder.tsx @@ -0,0 +1,32 @@ +import * as React from 'react'; +import { + Card, + CardBody, + CardHeader, + CardTitle, + EmptyState, + EmptyStateIcon, + Title, +} from '@patternfly/react-core'; +import { CubesIcon } from '@patternfly/react-icons'; + +type MetricsPlaceHolderProps = { + title: string; +}; +const MetricsPlaceHolder: React.FC = ({ title }) => ( + + + {title} + + + + + + Metrics coming soon + + + + +); + +export default MetricsPlaceHolder; diff --git a/frontend/src/pages/modelServing/screens/metrics/performance/ModelGraphs.tsx b/frontend/src/pages/modelServing/screens/metrics/performance/ModelGraphs.tsx index 0396eb76e2..521f8c7ee3 100644 --- a/frontend/src/pages/modelServing/screens/metrics/performance/ModelGraphs.tsx +++ b/frontend/src/pages/modelServing/screens/metrics/performance/ModelGraphs.tsx @@ -1,40 +1,14 @@ import * as React from 'react'; -import { Stack, StackItem } from '@patternfly/react-core'; -import MetricsChart from '~/pages/modelServing/screens/metrics/MetricsChart'; -import { - ModelMetricType, - ModelServingMetricsContext, -} from '~/pages/modelServing/screens/metrics/ModelServingMetricsContext'; -import { ContextResourceData, PrometheusQueryRangeResultValue } from '~/types'; +import { InferenceServiceKind } from '~/k8sTypes'; +import { isModelMesh } from '~/pages/modelServing/utils'; +import ModelMeshMetrics from '~/pages/modelServing/screens/metrics/performance/ModelMeshMetrics'; +import KserveMetrics from '~/pages/modelServing/screens/metrics/performance/KserveMetrics'; -const ModelGraphs: React.FC = () => { - const { data } = React.useContext(ModelServingMetricsContext); - - return ( - - - , - }, - { - name: 'Failed', - metric: data[ - ModelMetricType.REQUEST_COUNT_FAILED - ] as ContextResourceData, - }, - ]} - color="blue" - title="HTTP requests per 5 minutes" - isStack - /> - - - ); +type ModelGraphProps = { + model: InferenceServiceKind; }; +const ModelGraphs: React.FC = ({ model }) => + isModelMesh(model) ? : ; + export default ModelGraphs; diff --git a/frontend/src/pages/modelServing/screens/metrics/performance/ModelMeshMetrics.tsx b/frontend/src/pages/modelServing/screens/metrics/performance/ModelMeshMetrics.tsx new file mode 100644 index 0000000000..8f9c511223 --- /dev/null +++ b/frontend/src/pages/modelServing/screens/metrics/performance/ModelMeshMetrics.tsx @@ -0,0 +1,46 @@ +import * as React from 'react'; +import { Stack, StackItem } from '@patternfly/react-core'; +import MetricsChart from '~/pages/modelServing/screens/metrics/MetricsChart'; +import { + ModelMetricType, + ModelServingMetricsContext, +} from '~/pages/modelServing/screens/metrics/ModelServingMetricsContext'; +import { ContextResourceData, PrometheusQueryRangeResultValue } from '~/types'; +import EnsureMetricsAvailable from '~/pages/modelServing/screens/metrics/EnsureMetricsAvailable'; + +const ModelMeshMetrics: React.FC = () => { + const { data } = React.useContext(ModelServingMetricsContext); + + return ( + + + + , + }, + { + name: 'Failed', + metric: data[ + ModelMetricType.REQUEST_COUNT_FAILED + ] as ContextResourceData, + }, + ]} + color="blue" + title="HTTP requests per 5 minutes" + isStack + /> + + + + ); +}; + +export default ModelMeshMetrics; diff --git a/frontend/src/pages/modelServing/screens/metrics/performance/PerformanceTab.tsx b/frontend/src/pages/modelServing/screens/metrics/performance/PerformanceTab.tsx index 26712183d6..e8f7ea6a71 100644 --- a/frontend/src/pages/modelServing/screens/metrics/performance/PerformanceTab.tsx +++ b/frontend/src/pages/modelServing/screens/metrics/performance/PerformanceTab.tsx @@ -7,30 +7,28 @@ import { Stack, StackItem, } from '@patternfly/react-core'; -import { PendingIcon } from '@patternfly/react-icons'; +import { WarningTriangleIcon } from '@patternfly/react-icons'; import { InferenceServiceKind } from '~/k8sTypes'; import { SupportedArea, useIsAreaAvailable } from '~/concepts/areas'; import MetricsPageToolbar from '~/concepts/metrics/MetricsPageToolbar'; import { isModelMesh } from '~/pages/modelServing/utils'; import ModelGraphs from '~/pages/modelServing/screens/metrics/performance/ModelGraphs'; -import { ModelMetricType } from '~/pages/modelServing/screens/metrics/ModelServingMetricsContext'; -import EnsureMetricsAvailable from '~/pages/modelServing/screens/metrics/EnsureMetricsAvailable'; type PerformanceTabsProps = { model: InferenceServiceKind; }; const PerformanceTab: React.FC = ({ model }) => { - const kserve = !isModelMesh(model); + const modelMesh = isModelMesh(model); const kserveMetricsEnabled = useIsAreaAvailable(SupportedArea.K_SERVE_METRICS).status; - if (kserve && kserveMetricsEnabled) { + if (!modelMesh && !kserveMetricsEnabled) { return ( - + } + icon={} alt="" /> @@ -38,19 +36,14 @@ const PerformanceTab: React.FC = ({ model }) => { } return ( - - - - - - - - - - + + + + + + + + ); };