From 823107b34b44168a441da24bf2e1d9dab16e5aa0 Mon Sep 17 00:00:00 2001 From: Saravana Date: Thu, 9 Nov 2023 20:38:37 +0530 Subject: [PATCH] rebase --- frontend/src/app/AppRoutes.tsx | 4 +- .../pages/edgeMVP/AddModelToRegistryModal.tsx | 265 ++++++++++++++++++ .../src/pages/edgeMVP/EdgeModelRegistry.tsx | 50 ++++ .../src/pages/edgeMVP/EmptyModelRegistry.tsx | 25 ++ .../src/pages/edgeMVP/ImportModelButton.tsx | 21 ++ frontend/src/utilities/NavData.tsx | 6 + 6 files changed, 370 insertions(+), 1 deletion(-) create mode 100644 frontend/src/pages/edgeMVP/AddModelToRegistryModal.tsx create mode 100644 frontend/src/pages/edgeMVP/EdgeModelRegistry.tsx create mode 100644 frontend/src/pages/edgeMVP/EmptyModelRegistry.tsx create mode 100644 frontend/src/pages/edgeMVP/ImportModelButton.tsx diff --git a/frontend/src/app/AppRoutes.tsx b/frontend/src/app/AppRoutes.tsx index fc24d81c50..f48982e7a7 100644 --- a/frontend/src/app/AppRoutes.tsx +++ b/frontend/src/app/AppRoutes.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { Route, Routes } from 'react-router-dom'; import ApplicationsPage from '~/pages/ApplicationsPage'; import UnauthorizedError from '~/pages/UnauthorizedError'; +import EdgeModelRegistry from ''; import { useUser } from '~/redux/selectors'; const InstalledApplications = React.lazy( @@ -31,6 +32,7 @@ const CustomServingRuntimeRoutes = React.lazy( const GroupSettingsPage = React.lazy(() => import('../pages/groupSettings/GroupSettings')); const LearningCenterPage = React.lazy(() => import('../pages/learningCenter/LearningCenter')); const BYONImagesPage = React.lazy(() => import('../pages/BYONImages/BYONImages')); +const EdgeModelRegistry = React.lazy(() => import('../pages/edgeMVP/EdgeModelRegistry')); const NotFound = React.lazy(() => import('../pages/NotFound')); const DependencyMissingPage = React.lazy( @@ -80,7 +82,7 @@ const AppRoutes: React.FC = () => { } /> )} - + } /> } /> diff --git a/frontend/src/pages/edgeMVP/AddModelToRegistryModal.tsx b/frontend/src/pages/edgeMVP/AddModelToRegistryModal.tsx new file mode 100644 index 0000000000..c65000e9a9 --- /dev/null +++ b/frontend/src/pages/edgeMVP/AddModelToRegistryModal.tsx @@ -0,0 +1,265 @@ +import { Button, Checkbox, Form, FormGroup, FormSelect, FormSelectOption, Modal, ModalVariant, Radio, Stack, StackItem, TextInput } from '@patternfly/react-core'; +import * as React from 'react'; + +type AddModelToRegistryModalProps = { + isOpen: boolean; + onClose: (submit: boolean) => void; +} + +const AddModelToRegistryModal: React.FC = ({ isOpen, onClose }) => { + const [isDefault, setIsDefault] = React.useState(true); + const [modelName, setModelName] = React.useState(''); + const [modelFramework, setModelFramework] = React.useState('PyTorch'); + const [s3BucketData, setS3BucketData] = React.useState({ + accessKey: '', + secretKey: '', + endpoint: '', + region: '', + bucket: '', + path: '' + }); + const [gitRepoData, setGitRepoData] = React.useState({ + apiToken: '', + url: '', + branch: '', + modelDirectory: '' + }); + + const options = [ + { value: 'PyTorch', label: 'PyTorch', disabled: false, isPlaceholder: false }, + { value: 'TensorFlow', label: 'TensorFlow', disabled: false, isPlaceholder: false }, + { value: 'OpenVino', label: 'OpenVino', disabled: false, isPlaceholder: false }, + ]; + + const onChange = (value: string, _event: React.FormEvent) => { + setModelFramework(value); + }; + + return ( + onClose(false)} + actions={[ + , + + ]} + > +
+ + + + setModelName(name)} + /> + + + + + + {options.map((option, index) => ( + + ))} + + + + + + { + setIsDefault(true) + }} + id="model-registry-s3-bucket-radio" + name="s3-bucket-radio" + body={ + + + + setS3BucketData({ ...s3BucketData, accessKey })} + isDisabled={!isDefault} + style={{ width: '400px' }} + /> + + + + + setS3BucketData({ ...s3BucketData, secretKey })} + isDisabled={!isDefault} + style={{ width: '400px' }} + /> + + + + + setS3BucketData({ ...s3BucketData, endpoint })} + isDisabled={!isDefault} + style={{ width: '400px' }} + /> + + + + + setS3BucketData({ ...s3BucketData, region })} + isDisabled={!isDefault} + style={{ width: '400px' }} + /> + + + + + setS3BucketData({ ...s3BucketData, bucket })} + isDisabled={!isDefault} + style={{ width: '400px' }} + /> + + + + + setS3BucketData({ ...s3BucketData, path })} + isDisabled={!isDefault} + style={{ width: '400px' }} + /> + + + + } + /> + + + { + setIsDefault(false) + }} + id="model-registry-git-respository-radio" + name="git-repository-radio" + body={ + + + + setGitRepoData({ ...gitRepoData, apiToken })} + isDisabled={isDefault} + style={{ width: '400px' }} + /> + + + + + setGitRepoData({ ...gitRepoData, url })} + isDisabled={isDefault} + style={{ width: '400px' }} + /> + + + + + setGitRepoData({ ...gitRepoData, branch })} + isDisabled={isDefault} + style={{ width: '400px' }} + /> + + + + + setGitRepoData({ ...gitRepoData, modelDirectory })} + isDisabled={isDefault} + style={{ width: '400px' }} + /> + + + + } + /> + + + +
+
+ ) +} + +export default AddModelToRegistryModal; \ No newline at end of file diff --git a/frontend/src/pages/edgeMVP/EdgeModelRegistry.tsx b/frontend/src/pages/edgeMVP/EdgeModelRegistry.tsx new file mode 100644 index 0000000000..2bb901bfc7 --- /dev/null +++ b/frontend/src/pages/edgeMVP/EdgeModelRegistry.tsx @@ -0,0 +1,50 @@ +import * as React from 'react'; +import _ from 'lodash'; +import ApplicationsPage from '~/pages/ApplicationsPage'; +import { OdhApplication } from '~/types'; +import { useWatchComponents } from '~/utilities/useWatchComponents'; +import { + Bullseye, +} from '@patternfly/react-core'; +import EmptyModelRegistry from './EmptyModelRegistry'; + +type EdgeModelRegistryInnerProps = { + loaded: boolean; + loadError?: Error; + components: OdhApplication[]; +}; + +const EdgeModelRegistryInner: React.FC = React.memo(({ loaded, loadError, components }) => { + + return ( + + + + + + ) +}) + +EdgeModelRegistryInner.displayName = 'EdgeModelRegistryInner'; + +const EdgeModelRegistry: React.FC = () => { + const { components, loaded, loadError } = useWatchComponents(true); + + const sortedComponents = React.useMemo( + () => + _.cloneDeep(components).sort((a, b) => a.spec.displayName.localeCompare(b.spec.displayName)), + [components], + ); + + + return ( + + ); +}; + +export default EdgeModelRegistry; \ No newline at end of file diff --git a/frontend/src/pages/edgeMVP/EmptyModelRegistry.tsx b/frontend/src/pages/edgeMVP/EmptyModelRegistry.tsx new file mode 100644 index 0000000000..b9f7e5ac43 --- /dev/null +++ b/frontend/src/pages/edgeMVP/EmptyModelRegistry.tsx @@ -0,0 +1,25 @@ +import * as React from 'react'; +import { + EmptyState, + EmptyStateBody, + EmptyStateIcon, + Title +} from '@patternfly/react-core'; +import { PlusCircleIcon } from '@patternfly/react-icons'; +import ImportModelButton from './ImportModelButton'; + +const EmptyModelRegistry: React.FC = () => { + + return ( + + + + No models in the registry + + To get started, add a model to the model registry. Adding a model will also initiate a pipeline that will build the model and its dependencies into a container image and save that image in a container image registry. + + + ) +} + +export default EmptyModelRegistry; \ No newline at end of file diff --git a/frontend/src/pages/edgeMVP/ImportModelButton.tsx b/frontend/src/pages/edgeMVP/ImportModelButton.tsx new file mode 100644 index 0000000000..beb44e3931 --- /dev/null +++ b/frontend/src/pages/edgeMVP/ImportModelButton.tsx @@ -0,0 +1,21 @@ +import { Button } from '@patternfly/react-core'; +import * as React from 'react'; +import AddModelToRegistryModal from './AddModelToRegistryModal'; + +const ImportModelButton: React.FC = () => { + const [open, setOpen] = React.useState(false); + return ( + <> + + { + setOpen(false); + }} + /> + + ) +} + +export default ImportModelButton; \ No newline at end of file diff --git a/frontend/src/utilities/NavData.tsx b/frontend/src/utilities/NavData.tsx index 1ee1561360..810228df01 100644 --- a/frontend/src/utilities/NavData.tsx +++ b/frontend/src/utilities/NavData.tsx @@ -90,6 +90,11 @@ const useDSPipelinesNav = (): NavDataItem[] => { ]; }; +const useEdgeMVPNav = (): NavDataItem[] => [ + {id: "edgemvp", group: { id: 'edge', title: 'Edge MVP' }, children: [ + { id: 'edge-model-registry', label: 'Model registry', href: '/modelRegistry' } + ]} +] const useModelServingNav = (): NavDataItem[] => useAreaCheck(SupportedArea.MODEL_SERVING, [ { id: 'modelServing', label: 'Model Serving', href: '/modelServing' }, @@ -164,4 +169,5 @@ export const useBuildNavData = (): NavDataItem[] => [ ...useModelServingNav(), ...useResourcesNav(), ...useSettingsNav(), + ...useEdgeMVPNav() ];