From 0af57049523b629e70fb6011fd636faa3450ec88 Mon Sep 17 00:00:00 2001 From: agatha197 Date: Tue, 5 Nov 2024 22:53:32 +0900 Subject: [PATCH] refactor: parsing image data before 24.12 --- react/src/components/CustomizedImageList.tsx | 119 +++++------------- react/src/components/ImageList.tsx | 121 ++++++------------- react/src/components/ImageTags.tsx | 46 +++++++ react/src/hooks/index.tsx | 47 ++++++- 4 files changed, 159 insertions(+), 174 deletions(-) diff --git a/react/src/components/CustomizedImageList.tsx b/react/src/components/CustomizedImageList.tsx index b40ff93775..309519268f 100644 --- a/react/src/components/CustomizedImageList.tsx +++ b/react/src/components/CustomizedImageList.tsx @@ -1,9 +1,4 @@ import Flex from '../components/Flex'; -import { - BaseImageTags, - ConstraintTags, - LangTags, -} from '../components/ImageTags'; import TableColumnsSettingModal from '../components/TableColumnsSettingModal'; import { filterEmptyItem, @@ -17,6 +12,7 @@ import { useUpdatableState, } from '../hooks'; import AliasedImageDoubleTags from './AliasedImageDoubleTags'; +import { ImageTags } from './ImageTags'; import TextHighlighter from './TextHighlighter'; import { CustomizedImageListForgetAndUntagMutation } from './__generated__/CustomizedImageListForgetAndUntagMutation.graphql'; import { @@ -63,19 +59,8 @@ const CustomizedImageList: React.FC = ({ children }) => { const [inFlightImageId, setInFlightImageId] = useState(); const [imageSearch, setImageSearch] = useState(''); const [isPendingSearchTransition, startSearchTransition] = useTransition(); - const [ - , - { - getNamespace, - getImageLang, - getBaseVersion, - getBaseImage, - getConstraints, - tagAlias, - getLang, - getBaseImages, - }, - ] = useBackendAIImageMetaData(); + const [, { getBaseVersion, getBaseImages, getBaseImage, tagAlias, getTags }] = + useBackendAIImageMetaData(); const { customized_images } = useLazyLoadQuery( graphql` @@ -142,17 +127,14 @@ const CustomizedImageList: React.FC = ({ children }) => { fullName: getImageFullName(image) || '', digest: image?.digest || '', // ------------ need only before 24.12.0 ------------ - lang: image?.name ? getLang(image.name) : '', baseversion: getBaseVersion(getImageFullName(image) || ''), baseimage: image?.tag && image?.name ? getBaseImages(image.tag, image.name) : [], - constraints: - image?.tag && image?.labels - ? getConstraints( - image.tag, - image.labels as { key: string; value: string }[], - ) - : [], + tag: + getTags( + image?.tag || '', + image?.labels as Array<{ key: string; value: string }>, + ) || [], isCustomized: image?.tag ? image.tag.indexOf('customized') !== -1 : false, @@ -180,14 +162,13 @@ const CustomizedImageList: React.FC = ({ children }) => { const baseImagesMatch = _.some(curFilterValues.baseimage, (value) => regExp.test(value), ); - const constraintsMatch = _.some( - curFilterValues.constraints, - (constraint) => regExp.test(constraint), + const tagMatch = _.some( + curFilterValues.tag, + (tag) => regExp.test(tag.key) || regExp.test(tag.value), ); const customizedMatch = curFilterValues.isCustomized ? regExp.test('customized') : false; - const langMatch = regExp.test(curFilterValues.lang); const namespaceMatch = regExp.test(curFilterValues.namespace || ''); const fullNameMatch = regExp.test(curFilterValues.fullName); const tagsMatch = _.some( @@ -200,8 +181,7 @@ const CustomizedImageList: React.FC = ({ children }) => { return ( baseVersionMatch || baseImagesMatch || - constraintsMatch || - langMatch || + tagMatch || namespaceMatch || customizedMatch || fullNameMatch || @@ -300,39 +280,21 @@ const CustomizedImageList: React.FC = ({ children }) => { title: t('environment.Namespace'), key: 'name', dataIndex: 'name', - sorter: (a: CommittedImage, b: CommittedImage) => { - const namespaceA = getNamespace(getImageFullName(a) || ''); - const namespaceB = getNamespace(getImageFullName(b) || ''); - return namespaceA && namespaceB - ? namespaceA.localeCompare(namespaceB) - : 0; - }, - render: (text: string, row: CommittedImage) => ( - {getNamespace(getImageFullName(row) || '')} - ), - }, - !supportExtendedImageInfo && { - title: t('environment.Language'), - key: 'lang', - sorter: (a: CommittedImage, b: CommittedImage) => - localeCompare( - getImageLang(getImageFullName(a) || ''), - getImageLang(getImageFullName(b) || ''), - ), - render: (text: string, row: CommittedImage) => ( - + sorter: (a, b) => localeCompare(getImageFullName(a), getImageFullName(b)), + render: (text: string) => ( + {text} ), }, !supportExtendedImageInfo && { title: t('environment.Version'), key: 'baseversion', dataIndex: 'baseversion', - sorter: (a: CommittedImage, b: CommittedImage) => + sorter: (a, b) => localeCompare( getBaseVersion(getImageFullName(a) || ''), getBaseVersion(getImageFullName(b) || ''), ), - render: (text: string, row: CommittedImage) => ( + render: (text, row) => ( {getBaseVersion(getImageFullName(row) || '')} @@ -342,44 +304,29 @@ const CustomizedImageList: React.FC = ({ children }) => { title: t('environment.Base'), key: 'baseimage', dataIndex: 'baseimage', - sorter: (a: CommittedImage, b: CommittedImage) => + sorter: (a, b) => localeCompare( getBaseImage(getImageFullName(a) || ''), getBaseImage(getImageFullName(b) || ''), ), - render: (text: string, row: CommittedImage) => ( - + render: (text, row) => ( + + {tagAlias(getBaseImage(getImageFullName(row) || ''))} + ), }, !supportExtendedImageInfo && { - title: t('environment.Constraint'), - key: 'constraint', - dataIndex: 'constraint', - sorter: (a: CommittedImage, b: CommittedImage) => { - const requirementA = - a?.tag && b?.labels - ? getConstraints( - a?.tag, - a?.labels as { key: string; value: string }[], - )[0] || '' - : ''; - const requirementB = - b?.tag && b?.labels - ? getConstraints( - b?.tag, - b?.labels as { key: string; value: string }[], - )[0] || '' - : ''; - return localeCompare(requirementA, requirementB); - }, - render: (text: string, row: CommittedImage) => - row?.tag ? ( - } - highlightKeyword={imageSearch} - /> - ) : null, + title: t('environment.Tags'), + key: 'tag', + dataIndex: 'tag', + sorter: (a, b) => localeCompare(a?.tag, b?.tag), + render: (text, row) => ( + } + highlightKeyword={imageSearch} + /> + ), }, { title: t('environment.Digest'), diff --git a/react/src/components/ImageList.tsx b/react/src/components/ImageList.tsx index 48c2727f43..a7560545af 100644 --- a/react/src/components/ImageList.tsx +++ b/react/src/components/ImageList.tsx @@ -12,7 +12,7 @@ import { } from '../hooks'; import DoubleTag from './DoubleTag'; import ImageInstallModal from './ImageInstallModal'; -import { BaseImageTags, ConstraintTags, LangTags } from './ImageTags'; +import { ImageTags } from './ImageTags'; import ManageAppsModal from './ManageAppsModal'; import ManageImageResourceLimitModal from './ManageImageResourceLimitModal'; import ResourceNumber from './ResourceNumber'; @@ -45,18 +45,8 @@ export type EnvironmentImage = NonNullable< const ImageList: React.FC<{ style?: React.CSSProperties }> = ({ style }) => { const { t } = useTranslation(); const [selectedRows, setSelectedRows] = useState([]); - const [ - , - { - getNamespace, - getBaseVersion, - getLang, - getBaseImages, - getConstraints, - getBaseImage, - tagAlias, - }, - ] = useBackendAIImageMetaData(); + const [, { getBaseVersion, getBaseImages, getBaseImage, tagAlias, getTags }] = + useBackendAIImageMetaData(); const { token } = theme.useToken(); const [managingApp, setManagingApp] = useState(null); const [managingResourceLimit, setManagingResourceLimit] = @@ -262,82 +252,54 @@ const ImageList: React.FC<{ style?: React.CSSProperties }> = ({ style }) => { title: t('environment.Namespace'), key: 'name', dataIndex: 'name', - sorter: (a: EnvironmentImage, b: EnvironmentImage) => - localeCompare(getImageFullName(a), getImageFullName(b)), - render: (text: string, row: EnvironmentImage) => ( - - {getNamespace(getImageFullName(row) || '')} - + sorter: (a, b) => localeCompare(getImageFullName(a), getImageFullName(b)), + render: (text) => ( + {text} ), }, !supportExtendedImageInfo && { - title: t('environment.Language'), - key: 'lang', - dataIndex: 'lang', - sorter: (a: EnvironmentImage, b: EnvironmentImage) => - localeCompare(getLang(a.name ?? ''), getLang(b.name ?? '')), - render: (text: string, row: EnvironmentImage) => ( - + title: t('environment.Base'), + key: 'baseimage', + dataIndex: 'baseimage', + sorter: (a, b) => + localeCompare( + getBaseImage(getImageFullName(a) || ''), + getBaseImage(getImageFullName(b) || ''), + ), + render: (text, row) => ( + + {tagAlias(getBaseImage(getImageFullName(row) || ''))} + ), }, !supportExtendedImageInfo && { title: t('environment.Version'), key: 'baseversion', dataIndex: 'baseversion', - sorter: (a: EnvironmentImage, b: EnvironmentImage) => + sorter: (a, b) => localeCompare( getBaseVersion(getImageFullName(a) || ''), getBaseVersion(getImageFullName(b) || ''), ), - render: (text: string, row: EnvironmentImage) => ( + render: (text, row) => ( {getBaseVersion(getImageFullName(row) || '')} ), }, !supportExtendedImageInfo && { - title: t('environment.Base'), - key: 'baseimage', - dataIndex: 'baseimage', - sorter: (a: EnvironmentImage, b: EnvironmentImage) => - localeCompare( - getBaseImage(getImageFullName(a) || ''), - getBaseImage(getImageFullName(b) || ''), - ), - render: (text: string, row: EnvironmentImage) => ( - + title: t('environment.Tags'), + key: 'tag', + dataIndex: 'tag', + sorter: (a, b) => localeCompare(a?.tag, b?.tag), + render: (text, row) => ( + } + highlightKeyword={imageSearch} + /> ), }, - !supportExtendedImageInfo && { - title: t('environment.Constraint'), - key: 'constraint', - dataIndex: 'constraint', - sorter: (a: EnvironmentImage, b: EnvironmentImage) => { - const requirementA = - a?.tag && b?.labels - ? getConstraints( - a?.tag, - a?.labels as { key: string; value: string }[], - )[0] || '' - : ''; - const requirementB = - b?.tag && b?.labels - ? getConstraints( - b?.tag, - b?.labels as { key: string; value: string }[], - )[0] || '' - : ''; - return localeCompare(requirementA, requirementB); - }, - render: (text: string, row: EnvironmentImage) => - row?.tag ? ( - } - highlightKeyword={imageSearch} - /> - ) : null, - }, { title: t('environment.Digest'), dataIndex: 'digest', @@ -424,17 +386,14 @@ const ImageList: React.FC<{ style?: React.CSSProperties }> = ({ style }) => { fullName: getImageFullName(image) || '', digest: image?.digest || '', // ------------ need only before 24.12.0 ------------ - lang: image?.name ? getLang(image.name) : '', baseversion: getBaseVersion(getImageFullName(image) || ''), baseimage: image?.tag && image?.name ? getBaseImages(image.tag, image.name) : [], - constraints: - image?.tag && image?.labels - ? getConstraints( - image.tag, - image.labels as { key: string; value: string }[], - ) - : [], + tag: + getTags( + image?.tag || '', + image?.labels as Array<{ key: string; value: string }>, + ) || [], isCustomized: image?.tag ? image.tag.indexOf('customized') !== -1 : false, @@ -462,14 +421,13 @@ const ImageList: React.FC<{ style?: React.CSSProperties }> = ({ style }) => { const baseImagesMatch = _.some(curFilterValues.baseimage, (value) => regExp.test(value), ); - const constraintsMatch = _.some( - curFilterValues.constraints, - (constraint) => regExp.test(constraint), + const tagMatch = _.some( + curFilterValues.tag, + (tag) => regExp.test(tag.key) || regExp.test(tag.value), ); const customizedMatch = curFilterValues.isCustomized ? regExp.test('customized') : false; - const langMatch = regExp.test(curFilterValues.lang); const namespaceMatch = regExp.test(curFilterValues.namespace || ''); const fullNameMatch = regExp.test(curFilterValues.fullName); const tagsMatch = _.some( @@ -482,8 +440,7 @@ const ImageList: React.FC<{ style?: React.CSSProperties }> = ({ style }) => { return ( baseVersionMatch || baseImagesMatch || - constraintsMatch || - langMatch || + tagMatch || namespaceMatch || customizedMatch || fullNameMatch || diff --git a/react/src/components/ImageTags.tsx b/react/src/components/ImageTags.tsx index 91d0d47e74..2c3501d61e 100644 --- a/react/src/components/ImageTags.tsx +++ b/react/src/components/ImageTags.tsx @@ -161,3 +161,49 @@ const SessionKernelTags: React.FC<{ }; export default React.memo(SessionKernelTags); + +interface ImageTagsProps extends TagProps { + tag: string; + labels: Array<{ key: string; value: string }>; + highlightKeyword?: string; +} +export const ImageTags: React.FC = ({ + tag, + labels, + highlightKeyword, + ...props +}) => { + labels = labels || []; + const [, { getTags, tagAlias }] = useBackendAIImageMetaData(); + const tags = getTags(tag, labels); + return ( + + {_.map(tags, (tag: { key: string; value: string }, index) => { + const isCustomized = tag.key === 'Customized'; + return ( + + {tagAlias(tag.key)} + + ), + color: isCustomized ? 'cyan' : 'blue', + }, + { + label: ( + + {tag.value} + + ), + color: isCustomized ? 'cyan' : 'blue', + }, + ]} + /> + ); + })} + + ); +}; diff --git a/react/src/hooks/index.tsx b/react/src/hooks/index.tsx index a934fd2e7d..9c05269ccb 100644 --- a/react/src/hooks/index.tsx +++ b/react/src/hooks/index.tsx @@ -242,8 +242,8 @@ export const useBackendAIImageMetaData = () => { } return metadata?.tagAlias[lang] || lang; }, - getImageTags: (imageName: string) => { - // const { key, tags } = getImageMeta(imageName); + getImageTagStr: (imageName: string) => { + return _.last(_.split(_.split(imageName, '@')[0], ':')); }, getFilteredRequirementsTags: (imageName: string) => { const { tags } = getImageMeta(imageName); @@ -261,12 +261,16 @@ export const useBackendAIImageMetaData = () => { return customizedNameLabel; }, getBaseVersion: (imageName: string) => { - const { tags } = getImageMeta(imageName); - return tags[0]; + return ( + _.first(_.split(_.last(_.split(imageName, ':')), /[^a-zA-Z\d.]+/)) || + '' + ); }, getBaseImage: (imageName: string) => { - const { tags } = getImageMeta(imageName); - return tags[1]; + const splitByColon = _.split(imageName, ':'); + const beforeLastColon = _.join(_.initial(splitByColon), ':'); + const lastItemAfterSplitBySlash = _.last(_.split(beforeLastColon, '/')); + return lastItemAfterSplitBySlash || ''; }, getBaseImages: (tag: string, name: string) => { const tags = tag.split('-'); @@ -292,6 +296,37 @@ export const useBackendAIImageMetaData = () => { return baseImageArr; }, getImageMeta, + getTags: (tag: string, labels: Array<{ key: string; value: string }>) => { + // Split the tag by '-' and remove the first item + const tags = _.tail(_.split(tag, '-')); + const result: Array<{ key: string; value: string }> = []; + // Process each tag, including handling 'customized_' tags + _.forEach(tags, (currentTag) => { + if (_.startsWith(currentTag, 'customized_')) { + // Handle 'customized_' tag by finding the custom image name and adding it to the result + const customizedNameLabel = _.get( + _.find(labels, { key: 'ai.backend.customized-image.name' }), + 'value', + '', + ); + result.push({ key: 'Customized', value: customizedNameLabel }); + } else { + // Separate the alphabetic prefix from the rest for non-'customized_' tags + const match = /^([a-zA-Z]+)(.*)$/.exec(currentTag); + if (match) { + const [, key, value] = match; + // Ensure value is an empty string if undefined + result.push({ key, value: value || '' }); + } + } + }); + + // Remove any entries with an empty 'key' and avoid adding 'customized_' tag more than once + return _.uniqWith( + _.filter(result, ({ key }) => !_.isEmpty(key)), + _.isEqual, + ); + }, getConstraints: ( tag: string, labels: { key: string; value: string }[],