diff --git a/react/src/components/EnvVarFormList.tsx b/react/src/components/EnvVarFormList.tsx
index 0286a3f8ce..b08ac94902 100644
--- a/react/src/components/EnvVarFormList.tsx
+++ b/react/src/components/EnvVarFormList.tsx
@@ -165,7 +165,7 @@ export function isSensitiveEnv(key: string) {
}
export function sanitizeSensitiveEnv(envs: EnvVarFormListValue[]) {
- return envs.map((env) => {
+ return _.map(envs, (env) => {
if (env && isSensitiveEnv(env.variable)) {
return { ...env, value: '' };
}
diff --git a/react/src/components/ImageEnvironmentSelectFormItems.tsx b/react/src/components/ImageEnvironmentSelectFormItems.tsx
index 8fc325c3a4..3e5027ed16 100644
--- a/react/src/components/ImageEnvironmentSelectFormItems.tsx
+++ b/react/src/components/ImageEnvironmentSelectFormItems.tsx
@@ -97,7 +97,7 @@ const ImageEnvironmentSelectFormItems: React.FC<
const [environmentSearch, setEnvironmentSearch] = useState('');
const [versionSearch, setVersionSearch] = useState('');
const { t } = useTranslation();
- const [metadata, { getImageMeta }] = useBackendAIImageMetaData();
+ const [metadata, { getImageMeta, tagAlias }] = useBackendAIImageMetaData();
const { token } = theme.useToken();
const { isDarkMode } = useThemeMode();
@@ -129,6 +129,12 @@ const ImageEnvironmentSelectFormItems: React.FC<
value
}
namespace @since(version: "24.09.1")
+ base_image_name @since(version: "24.09.1")
+ tags @since(version: "24.09.1") {
+ key
+ value
+ }
+ version @since(version: "24.09.1")
}
}
`,
@@ -498,7 +504,7 @@ const ImageEnvironmentSelectFormItems: React.FC<
}}
/>
- {environmentGroup.displayName}
+ {tagAlias(environmentGroup.displayName)}
- {t('session.launcher.Version')}
-
- {t('session.launcher.Base')}
-
- {t('session.launcher.Architecture')}
-
- {t('session.launcher.Requirements')}
+ {supportExtendedImageInfo ? (
+ <>
+ {t('session.launcher.Version')}
+
+ {t('session.launcher.Architecture')}
+
+ {t('session.launcher.Tags')}
+ >
+ ) : (
+ <>
+ {t('session.launcher.Version')}
+
+ {t('session.launcher.Base')}
+
+ {t('session.launcher.Architecture')}
+
+ {t('session.launcher.Requirements')}
+ >
+ )}
{menu}
@@ -602,18 +620,21 @@ const ImageEnvironmentSelectFormItems: React.FC<
'-',
) || ['', '', ''];
- let tagAlias = metadata?.tagAlias[tag];
- if (!tagAlias) {
+ let metadataTagAlias = metadata?.tagAlias[tag];
+ if (!metadataTagAlias) {
for (const [key, replaceString] of Object.entries(
metadata?.tagReplace || {},
)) {
const pattern = new RegExp(key);
if (pattern.test(tag)) {
- tagAlias = tag?.replace(pattern, replaceString);
+ metadataTagAlias = tag?.replace(
+ pattern,
+ replaceString,
+ );
}
}
- if (!tagAlias) {
- tagAlias = tag;
+ if (!metadataTagAlias) {
+ metadataTagAlias = tag;
}
}
@@ -695,39 +716,98 @@ const ImageEnvironmentSelectFormItems: React.FC<
value={getImageFullName(image)}
filterValue={[
version,
- tagAlias,
+ metadataTagAlias,
image?.architecture,
...extraFilterValues,
].join('\t')}
>
-
+ {supportExtendedImageInfo ? (
- {version}
-
-
-
- {tagAlias}
+ {image?.version}
{image?.architecture}
+
+
+ {/* TODO: replace this with AliasedImageDoubleTags after image list query with ImageNode is implemented. */}
+ {_.map(
+ image?.tags,
+ (tag: { key: string; value: string }) => {
+ const isCustomized = _.includes(
+ tag.key,
+ 'customized_',
+ );
+ const tagValue = isCustomized
+ ? _.find(image?.labels, {
+ key: 'ai.backend.customized-image.name',
+ })?.value
+ : tag.value;
+ return (
+
+ {tagAlias(tag.key)}
+
+ ),
+ color: isCustomized ? 'cyan' : 'blue',
+ },
+ {
+ label: (
+
+ {tagValue}
+
+ ),
+ color: isCustomized ? 'cyan' : 'blue',
+ },
+ ]}
+ />
+ );
+ },
+ )}
+
-
- {requirementTags || '-'}
+ ) : (
+
+
+
+ {version}
+
+
+
+ {metadataTagAlias}
+
+
+
+ {image?.architecture}
+
+
+
+ {requirementTags || '-'}
+
-
+ )}
);
},
diff --git a/react/src/components/ImageList.tsx b/react/src/components/ImageList.tsx
index 9ad821a7bb..e9e4bc802c 100644
--- a/react/src/components/ImageList.tsx
+++ b/react/src/components/ImageList.tsx
@@ -339,6 +339,7 @@ const ImageList: React.FC<{ style?: React.CSSProperties }> = ({ style }) => {
}
+ highlightKeyword={imageSearch}
/>
) : null,
},
diff --git a/react/src/components/ResourceAllocationFormItems.test.ts b/react/src/components/ResourceAllocationFormItems.test.ts
index be592d9055..5cd4e965c7 100644
--- a/react/src/components/ResourceAllocationFormItems.test.ts
+++ b/react/src/components/ResourceAllocationFormItems.test.ts
@@ -46,6 +46,9 @@ describe('getAllocatablePresetNames', () => {
registry: 'registry1',
tag: 'tag1',
resource_limits: [{ key: 'cuda.shares', min: '1', max: '1' }],
+ base_image_name: undefined,
+ tags: undefined,
+ version: undefined,
};
it('should return presets when currentImage has accelerator limits', () => {
diff --git a/react/src/hooks/useBackendAIImageMetaData.test.tsx b/react/src/hooks/useBackendAIImageMetaData.test.tsx
index df4ced5a7f..11ca50bb1b 100644
--- a/react/src/hooks/useBackendAIImageMetaData.test.tsx
+++ b/react/src/hooks/useBackendAIImageMetaData.test.tsx
@@ -145,6 +145,9 @@ describe('useBackendAIImageMetaData', () => {
value: 'NVIDIA CORPORATION ',
},
],
+ base_image_name: undefined,
+ tags: undefined,
+ version: undefined,
}) || '',
);
expect(key).toBe('training');
diff --git a/react/src/pages/SessionLauncherPage.tsx b/react/src/pages/SessionLauncherPage.tsx
index b63de932d6..4d96e2b84d 100644
--- a/react/src/pages/SessionLauncherPage.tsx
+++ b/react/src/pages/SessionLauncherPage.tsx
@@ -39,9 +39,11 @@ import VFolderTableFormItem, {
import {
compareNumberWithUnits,
generateRandomString,
+ getImageFullName,
iSizeToSize,
} from '../helper';
import {
+ useBackendAIImageMetaData,
useSuspendedBackendaiClient,
useUpdatableState,
useWebUINavigate,
@@ -73,6 +75,7 @@ import {
Checkbox,
Col,
Descriptions,
+ Divider,
Form,
Grid,
Input,
@@ -184,6 +187,10 @@ const SessionLauncherPage = () => {
const currentUserRole = useCurrentUserRole();
const [currentGlobalResourceGroup, setCurrentGlobalResourceGroup] =
useCurrentResourceGroupState();
+ const [, { tagAlias }] = useBackendAIImageMetaData();
+
+ const supportExtendedImageInfo =
+ baiClient?.supports('extended-image-info') ?? false;
const [isStartingSession, setIsStartingSession] = useState(false);
const INITIAL_FORM_VALUES: DeepPartial = useMemo(
@@ -1295,67 +1302,188 @@ const SessionLauncherPage = () => {
{currentProject.name}
-
-
-
-
-
- {/* {form.getFieldValue('environments').image} */}
-
- {form.getFieldValue('environments')?.manual ? (
-
- {form.getFieldValue('environments')?.manual}
-
- ) : (
- <>
-
+
+
+
+
+
+ {form.getFieldValue('environments')
+ ?.manual ? (
+
+ {
form.getFieldValue('environments')
- ?.version
+ ?.manual
}
- />
- {form.getFieldValue('environments')
- ?.customizedTag ? (
-
+ ) : (
+ <>
+
+ {tagAlias(
+ form.getFieldValue('environments')
+ ?.image?.base_image_name,
+ )}
+
+
+
+ {
+ form.getFieldValue('environments')
+ ?.image?.version
+ }
+
+
+
+ {
+ form.getFieldValue('environments')
+ ?.image?.architecture
+ }
+
+
+ {/* TODO: replace this with AliasedImageDoubleTags after image list query with ImageNode is implemented. */}
+ {_.map(
+ form.getFieldValue('environments')
+ ?.image?.tags,
+ (tag: {
+ key: string;
+ value: string;
+ }) => {
+ const isCustomized = _.includes(
+ tag.key,
+ 'customized_',
+ );
+ const tagValue = isCustomized
+ ? _.find(
+ form.getFieldValue(
+ 'environments',
+ )?.image?.labels,
+ {
+ key: 'ai.backend.customized-image.name',
+ },
+ )?.value
+ : tag.value;
+ return (
+
+ );
+ },
+ )}
+
- ) : null}
+ >
+ )}
+
+
+
+ ) : (
+
+
+
+
+
+ {/* {form.getFieldValue('environments').image} */}
+
+ {form.getFieldValue('environments')
+ ?.manual ? (
- >
- )}
-
-
-
+ >
+ {
+ form.getFieldValue('environments')
+ ?.manual
+ }
+
+ ) : (
+ <>
+
+ {form.getFieldValue('environments')
+ ?.customizedTag ? (
+
+ ) : null}
+
+ >
+ )}
+
+
+
+ )}
{form.getFieldValue('envvars')?.length > 0 && (