From f07e70d04512bd9a0e9a86587ad2d6c4ee3e112d Mon Sep 17 00:00:00 2001 From: agatha197 <28584164+agatha197@users.noreply.github.com> Date: Sat, 21 Sep 2024 21:20:40 +0000 Subject: [PATCH] feat: Improve form item and switch to README preview on validation check (#2715) **Changes:** This PR updates the ImportFromHuggingFaceModal component and related translations to enhance the user experience when importing models from Hugging Face. Key changes include: 1. Added new form fields for "Model store folder name" and "Service name". 2. Implemented a README.md preview card with Markdown support. 3. Added validation to ensure only LLM models (text-generation pipeline) are supported. 4. Updated translations for new strings across multiple languages. **Rationale:** These changes provide users with more control over the import process and better information about the model being imported. The README preview allows users to review model details before importing, while the additional form fields enable customization of the import location and service name. **Effects:** - Users can now specify custom folder names and service names for imported models. - The UI now displays a preview of the model's README, improving user understanding of the model before import. - The system now restricts imports to LLM models only, preventing potential issues with unsupported model types. - Improved localization support for the new features across multiple languages. **Screenshots:** - Default ![image.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/2HueYSdFvL8pOB5mgrUQ/22e37a3e-2eb1-48dd-b36d-36999677273c.png) - Valid ![image.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/2HueYSdFvL8pOB5mgrUQ/cc02186d-b148-4576-b2d1-686e25958bb5.png) - Invalid URL ![image.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/2HueYSdFvL8pOB5mgrUQ/2e4e0e40-1295-41e1-86b4-7e00a1049943.png) - Not LLM model ![image.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/2HueYSdFvL8pOB5mgrUQ/71bfc317-6295-438f-aeee-3cc4057885bb.png) **Checklist:** - [ ] Documentation - [ ] Test case(s) to demonstrate the difference of before/after --- .../components/ImportFromHuggingFaceModal.tsx | 124 +++++++++++++----- resources/i18n/de.json | 5 +- resources/i18n/el.json | 5 +- resources/i18n/en.json | 5 +- resources/i18n/es.json | 5 +- resources/i18n/fi.json | 5 +- resources/i18n/fr.json | 5 +- resources/i18n/id.json | 5 +- resources/i18n/it.json | 5 +- resources/i18n/ja.json | 5 +- resources/i18n/ko.json | 5 +- resources/i18n/mn.json | 5 +- resources/i18n/ms.json | 5 +- resources/i18n/pl.json | 5 +- resources/i18n/pt-BR.json | 5 +- resources/i18n/pt.json | 5 +- resources/i18n/ru.json | 5 +- resources/i18n/th.json | 5 +- resources/i18n/tr.json | 5 +- resources/i18n/vi.json | 5 +- resources/i18n/zh-CN.json | 5 +- resources/i18n/zh-TW.json | 5 +- 22 files changed, 178 insertions(+), 51 deletions(-) diff --git a/react/src/components/ImportFromHuggingFaceModal.tsx b/react/src/components/ImportFromHuggingFaceModal.tsx index 36777e2cb..8689b2880 100644 --- a/react/src/components/ImportFromHuggingFaceModal.tsx +++ b/react/src/components/ImportFromHuggingFaceModal.tsx @@ -3,10 +3,12 @@ import { useSuspendedBackendaiClient } from '../hooks'; import { useSuspenseTanQuery } from '../hooks/reactQueryAlias'; import BAIModal, { BAIModalProps } from './BAIModal'; import Flex from './Flex'; +import { FilterOutlined } from '@ant-design/icons'; import { useToggle } from 'ahooks'; import { Button, - Descriptions, + Card, + Empty, Form, FormInstance, Input, @@ -17,13 +19,44 @@ import { } from 'antd'; import _ from 'lodash'; import { CheckIcon } from 'lucide-react'; -import React, { useEffect, useRef, useState, useTransition } from 'react'; +import Markdown from 'markdown-to-jsx'; +import React, { + Suspense, + useEffect, + useRef, + useState, + useTransition, +} from 'react'; import { useTranslation } from 'react-i18next'; type Service = { url: string; - inference_engine_version?: string; - replica_number?: number; + service_name?: string; + folder_name?: string; +}; + +const ReadmeFallbackCard = () => { + const { token } = theme.useToken(); + return ( + + + README.md + + } + styles={{ + body: { + padding: token.paddingLG, + overflow: 'auto', + height: 200, + }, + }} + > + + + ); }; interface ImportFromHuggingFaceModalProps extends BAIModalProps { @@ -43,10 +76,11 @@ const ImportFromHuggingFaceModal: React.FC = ({ const baiClient = useSuspendedBackendaiClient(); const [isPendingCheck, startCheckTransition] = useTransition(); - const hugginFaceModelInfo = useSuspenseTanQuery<{ + const huggingFaceModelInfo = useSuspenseTanQuery<{ author?: string; model_name?: string; markdown?: string; + pipeline_tag?: string; isError?: boolean; url?: string; }>({ @@ -74,7 +108,7 @@ const ImportFromHuggingFaceModal: React.FC = ({ }, }); const isHuggingfaceURLExisted = !_.isEmpty( - hugginFaceModelInfo.data.model_name, + huggingFaceModelInfo.data.model_name, ); const shouldSkipURLCheck = isHuggingfaceURLExisted && huggingFaceURL === typedURL; @@ -89,10 +123,10 @@ const ImportFromHuggingFaceModal: React.FC = ({ // validate when huggingFaceModelInfo is updated useEffect(() => { - if (hugginFaceModelInfo.data.url) { + if (huggingFaceModelInfo.data.url) { formRef.current?.validateFields().catch(() => {}); } - }, [hugginFaceModelInfo.data.url]); + }, [huggingFaceModelInfo.data.url]); const handleOnClick = () => { formRef.current @@ -124,7 +158,11 @@ const ImportFromHuggingFaceModal: React.FC = ({ type="primary" htmlType="submit" onClick={handleOnClick} - disabled={!shouldSkipURLCheck} + disabled={ + !shouldSkipURLCheck || + (!_.isEmpty(huggingFaceModelInfo.data?.pipeline_tag) && + huggingFaceModelInfo.data?.pipeline_tag !== 'text-generation') + } > {isImportOnly ? t('data.modelStore.Import') @@ -141,7 +179,7 @@ const ImportFromHuggingFaceModal: React.FC = ({ layout="vertical" requiredMark="optional" > - + = ({ ]} > { handleOnCheck(); }} @@ -184,22 +221,30 @@ const ImportFromHuggingFaceModal: React.FC = ({ name="" rules={[ { - validator: async (_, value) => { + validator: async () => { if ( !isHuggingfaceURLExisted && - hugginFaceModelInfo.data?.isError && - hugginFaceModelInfo.data.url === typedURL + huggingFaceModelInfo.data?.isError && + huggingFaceModelInfo.data.url === typedURL ) { return Promise.reject( t('data.modelStore.InvalidHuggingFaceUrl'), ); } else { - if (shouldSkipURLCheck) { - return Promise.resolve(); - } else { + if (!shouldSkipURLCheck) { return Promise.reject( t('data.modelStore.InvalidHuggingFaceUrl'), ); + } else if ( + !_.isEmpty(huggingFaceModelInfo.data?.pipeline_tag) && + huggingFaceModelInfo.data?.pipeline_tag !== + 'text-generation' + ) { + return Promise.reject( + t('data.modelStore.NotSupportedModel'), + ); + } else { + return Promise.resolve(); } } }, @@ -207,20 +252,39 @@ const ImportFromHuggingFaceModal: React.FC = ({ ]} > - - - {shouldSkipURLCheck && hugginFaceModelInfo.data?.model_name} - - - {shouldSkipURLCheck && hugginFaceModelInfo.data?.author} - - + + + + + + {huggingFaceURL && huggingFaceModelInfo.data?.markdown ? ( + }> + + + README.md + + } + styles={{ + body: { + padding: token.paddingLG, + overflow: 'auto', + height: 200, + }, + }} + > + {huggingFaceModelInfo.data?.markdown} + + + ) : ( + + )}