diff --git a/src/components/audio-player/simple-audio.tsx b/src/components/audio-player/simple-audio.tsx index 8492593c..9b39bc32 100644 --- a/src/components/audio-player/simple-audio.tsx +++ b/src/components/audio-player/simple-audio.tsx @@ -1,7 +1,7 @@ import { formatTime } from '@/utils/index'; import { DeleteOutlined, DownloadOutlined } from '@ant-design/icons'; import { useIntl } from '@umijs/max'; -import { Button, Dropdown, Slider, type MenuProps } from 'antd'; +import { Button, Dropdown, Slider, Tooltip, type MenuProps } from 'antd'; import { createStyles } from 'antd-style'; import { round } from 'lodash'; import React, { @@ -20,7 +20,7 @@ interface AudioPlayerProps { url: string; speed?: number; ref?: any; - name?: string; + name: string; height?: number; width?: number; duration?: number; @@ -107,6 +107,7 @@ const AudioPlayer: React.FC = forwardRef((props, ref) => { autoplay = false, speed: defaultSpeed = 1, actions = ['delete'], + name, onDelete } = props; const audioRef = React.useRef(null); @@ -117,6 +118,7 @@ const AudioPlayer: React.FC = forwardRef((props, ref) => { currentTime: 0, duration: 0 }); + console.log('audioState', name); const [playOn, setPlayOn] = React.useState(false); const [speakerOn, setSpeakerOn] = React.useState(false); const [volume, setVolume] = React.useState(1); @@ -246,7 +248,7 @@ const AudioPlayer: React.FC = forwardRef((props, ref) => { const link = document.createElement('a'); link.href = url; - link.download = 'audio.mp3'; // Default filename + link.download = filename || 'audio.mp3'; // Default filename document.body.appendChild(link); link.click(); link.remove(); @@ -304,7 +306,11 @@ const AudioPlayer: React.FC = forwardRef((props, ref) => { return (
- - {formatTime(audioState.currentTime)} /{' '} - {formatTime(audioState.duration)} - + + + {formatTime(audioState.currentTime)} /{' '} + {formatTime(audioState.duration)} + +
= (props) => { const intl = useIntl(); return ( - - } - size="small" - onClick={onExport} - > - {intl.formatMessage({ id: 'common.button.export' })} - - - - + + + } + size="small" + onClick={onExport} + > + {intl.formatMessage({ id: 'common.button.export' })} + + + + + ); }; diff --git a/src/pages/llmodels/components/deploy-modal.tsx b/src/pages/llmodels/components/deploy-modal.tsx index c33404b4..0705de97 100644 --- a/src/pages/llmodels/components/deploy-modal.tsx +++ b/src/pages/llmodels/components/deploy-modal.tsx @@ -112,6 +112,7 @@ const AddModal: FC = (props) => { unlockWarningStatus, handleOnValuesChange: handleOnValuesChangeBefore, handleEvaluateOnChange, + clearCahceFormValues, warningStatus, submitAnyway } = useCheckCompatibility(); @@ -123,13 +124,6 @@ const AddModal: FC = (props) => { const [collapsed, setCollapsed] = useState(false); const [isGGUF, setIsGGUF] = useState(false); const modelFileRef = useRef(null); - const isHolderRef = useRef<{ - model: boolean; - file: boolean; - }>({ - model: false, - file: false - }); const evaluateStateRef = useRef<{ state: EvaluateProccessType }>({ state: 'form' }); @@ -141,6 +135,7 @@ const AddModal: FC = (props) => { */ const updateRequestModelId = () => { requestModelIdRef.current += 1; + return requestModelIdRef.current; }; /** @@ -152,17 +147,6 @@ const AddModal: FC = (props) => { evaluateStateRef.current.state = state; }; - /** - * - * @param flag set the evaluate status of the model or file - */ - const setIsHolderRef = (flag: Record) => { - isHolderRef.current = { - ...isHolderRef.current, - ...flag - }; - }; - const handleOnValuesChange = (data: { changedValues: any; allValues: any; @@ -188,29 +172,30 @@ const AddModal: FC = (props) => { return categories || null; }; - const handleSelectModelFile = async (item: any, evaluate?: boolean) => { + const handleSelectModelFile = async (item: any) => { form.current?.form?.resetFields(resetFieldsByFile); const modelInfo = onSelectModel(selectedModel, props.source); - /** display the selected model file information, but not - * unitl the evaluate result is ready - */ form.current?.setFieldsValue?.({ ...modelInfo, file_name: item.fakeName, categories: getCategory(item) }); + console.log('handleSelectModelFile', item); + await new Promise((resolve) => { setTimeout(() => { resolve(true); }, 0); }); + // evaluate the form data when select a model file if (item.fakeName) { unlockWarningStatus(); - const currentModelId = requestModelIdRef.current; + const currentModelId = updateRequestModelId(); setEvaluteState(EvaluateProccess.file); + const evaluateRes = await handleEvaluateOnChange?.({ changedValues: {}, allValues: form.current?.form?.getFieldsValue?.(), @@ -245,41 +230,68 @@ const AddModal: FC = (props) => { } }; - const handleOnSelectModel = (item: any, evaluate?: boolean) => { - /** - * evaluate: false means select a new model - * evaluate: true means select a model file from the evaluate result - */ - updateRequestModelId(); + const handleOnSelectModel = async (item: any) => { + // If the item is empty or the same as the selected model, do nothing + console.log('handleOnSelectModel', item, selectedModel); + if ( + _.isEmpty(item) || + (item.isGGUF === selectedModel.isGGUF && item.name === selectedModel.name) + ) { + return; + } + setIsGGUF(item.isGGUF); + clearCahceFormValues(); + unlockWarningStatus(); + setEvaluteState(EvaluateProccess.model); + setSelectedModel(item); - // If the evaluate is false, it means that the user selects a new model or the first time to open the modal. - if (!evaluate) { - unlockWarningStatus(); - setEvaluteState(EvaluateProccess.model); - setSelectedModel(item); - form.current?.form?.resetFields(resetFieldsByModel); - const modelInfo = onSelectModel(item, props.source); + form.current?.form?.resetFields(resetFieldsByModel); + const modelInfo = onSelectModel(item, props.source); + form.current?.setFieldsValue?.({ + ...modelInfo, + categories: getCategory(item) + }); + + setWarningStatus( + { + show: true, + title: '', + type: 'transition', + message: intl.formatMessage({ id: 'models.form.evaluating' }) + }, + { + override: true + } + ); + + await new Promise((resolve) => { + setTimeout(() => { + resolve(true); + }, 0); + }); + + if (item.isGGUF) { + modelFileRef.current?.fetchModelFiles?.(); + } + }; + + const handleOnSelectModelAfterEvaluate = (item: any) => { + console.log('handleOnSelectModelAfterEvaluate', item); + if (item.isGGUF) { + return; + } + const modelInfo = onSelectModel(item, props.source); + if ( + evaluateStateRef.current.state === EvaluateProccess.model && + item.evaluateResult + ) { + handleShowCompatibleAlert(item.evaluateResult); form.current?.setFieldsValue?.({ + ...getDefaultSpec(item), ...modelInfo, categories: getCategory(item) }); } - - if (!item.isGGUF) { - setIsGGUF(false); - const modelInfo = onSelectModel(item, props.source); - if ( - evaluateStateRef.current.state === EvaluateProccess.model && - item.evaluateResult - ) { - handleShowCompatibleAlert(item.evaluateResult); - form.current?.setFieldsValue?.({ - ...getDefaultSpec(item), - ...modelInfo, - categories: getCategory(item) - }); - } - } }; const handleOnOk = async (allValues: FormData) => { @@ -298,14 +310,6 @@ const AddModal: FC = (props) => { const handleSetIsGGUF = async (flag: boolean) => { setIsGGUF(flag); - await new Promise((resolve) => { - setTimeout(() => { - resolve(true); - }, 0); - }); - if (flag) { - modelFileRef.current?.fetchModelFiles?.(); - } }; const handleBackendChange = async (backend: string) => { @@ -446,6 +450,9 @@ const AddModal: FC = (props) => { hasLinuxWorker={hasLinuxWorker} modelSource={props.source} onSelectModel={handleOnSelectModel} + onSelectModelAfterEvaluate={ + handleOnSelectModelAfterEvaluate + } displayEvaluateStatus={displayEvaluateStatus} unlockWarningStatus={unlockWarningStatus} gpuOptions={props.gpuOptions} diff --git a/src/pages/llmodels/components/file-skeleton.tsx b/src/pages/llmodels/components/file-skeleton.tsx new file mode 100644 index 00000000..37c0cd7f --- /dev/null +++ b/src/pages/llmodels/components/file-skeleton.tsx @@ -0,0 +1,23 @@ +import { Flex, Skeleton, Space } from 'antd'; +import styled from 'styled-components'; + +const Wrapper = styled(Flex)` + padding: 12px 14px; + border: 1px solid var(--ant-color-border); + border-radius: var(--border-radius-base); + background-color: var(--ant-color-bg-container); +`; + +const FileSkeleton = () => { + return ( + + + + + + + + ); +}; + +export default FileSkeleton; diff --git a/src/pages/llmodels/components/hf-model-file.tsx b/src/pages/llmodels/components/hf-model-file.tsx index a7614086..6d67fa84 100644 --- a/src/pages/llmodels/components/hf-model-file.tsx +++ b/src/pages/llmodels/components/hf-model-file.tsx @@ -21,6 +21,7 @@ import { } from '../apis'; import { backendOptionsMap, modelSourceMap } from '../config'; import '../style/hf-model-file.less'; +import FileSkeleton from './file-skeleton'; import ModelFileItem from './model-file-item'; import TitleWrapper from './title-wrapper'; @@ -40,7 +41,8 @@ interface HFModelFileProps { modelSource: string; ref: any; gpuOptions?: any[]; - onSelectFile?: (file: any, evaluate?: boolean) => void; + onSelectFile?: (file: any) => void; + onSelectFileAfterEvaluate?: (file: any) => void; } const pattern = /^(.*)-(\d+)-of-(\d+)\.(.*)$/; @@ -50,7 +52,8 @@ const includeReg = /\.(safetensors|gguf)$/i; const filterRegGGUF = /\.(gguf)$/i; const HFModelFile: React.FC = forwardRef((props, ref) => { - const { collapsed, modelSource, isDownload } = props; + const { collapsed, modelSource, isDownload, onSelectFileAfterEvaluate } = + props; const intl = useIntl(); const [isEvaluating, setIsEvaluating] = useState(false); const [dataSource, setDataSource] = useState({ @@ -74,8 +77,8 @@ const HFModelFile: React.FC = forwardRef((props, ref) => { const checkTokenRef = useRef(null); const timer = useRef(null); - const handleSelectModelFile = (item: any, evaluate?: boolean) => { - props.onSelectFile?.(item, evaluate); + const handleSelectModelFile = (item: any) => { + props.onSelectFile?.(item); setCurrent(item.path); currentPathRef.current = item.path; }; @@ -258,7 +261,7 @@ const HFModelFile: React.FC = forwardRef((props, ref) => { ); if (currentItem) { - handleSelectModelFile(currentItem, true); + onSelectFileAfterEvaluate?.(currentItem); } setDataSource({ fileList: resultList, loading: false }); setIsEvaluating(false); @@ -361,7 +364,13 @@ const HFModelFile: React.FC = forwardRef((props, ref) => { )}
- {dataSource.fileList.length ? ( + {dataSource.loading ? ( + + {_.times(5, (index: number) => { + return ; + })} + + ) : dataSource.fileList.length ? ( {_.map(dataSource.fileList, (item: any) => { return ( @@ -376,16 +385,13 @@ const HFModelFile: React.FC = forwardRef((props, ref) => { })} ) : ( - !dataSource.loading && - !dataSource.fileList.length && ( - - ) + )}
diff --git a/src/pages/llmodels/components/model-card.tsx b/src/pages/llmodels/components/model-card.tsx index 3f51ee3e..fb22ffe2 100644 --- a/src/pages/llmodels/components/model-card.tsx +++ b/src/pages/llmodels/components/model-card.tsx @@ -292,8 +292,16 @@ const ModelCard: React.FC<{ ); useEffect(() => { + if (!props.selectedModel) return; + getModelCardData(); - }, [props.selectedModel?.name]); + setIsGGUFModel(props.selectedModel.isGGUF); + setModelData({ + id: props.selectedModel.name, + name: props.selectedModel.name, + isGGUF: props.selectedModel.isGGUF + }); + }, [props.selectedModel?.name, props.selectedModel?.isGGUF]); useEffect(() => { return () => { diff --git a/src/pages/llmodels/components/search-model.tsx b/src/pages/llmodels/components/search-model.tsx index 70fab7c5..19ada758 100644 --- a/src/pages/llmodels/components/search-model.tsx +++ b/src/pages/llmodels/components/search-model.tsx @@ -46,7 +46,8 @@ interface SearchInputProps { gpuOptions?: any[]; setLoadingModel?: (flag: boolean) => void; onSourceChange?: (source: string) => void; - onSelectModel: (model: any, evaluate?: boolean) => void; + onSelectModel: (model: any) => void; + onSelectModelAfterEvaluate: (model: any) => void; unlockWarningStatus?: () => void; displayEvaluateStatus?: ( data: MessageStatus, @@ -63,6 +64,7 @@ const SearchModel: React.FC = (props) => { gpuOptions, setLoadingModel, onSelectModel, + onSelectModelAfterEvaluate, displayEvaluateStatus, unlockWarningStatus } = props; @@ -138,9 +140,13 @@ const SearchModel: React.FC = (props) => { return isGGUF || isGGUFFromMs; }; - const handleOnSelectModel = (model: any, evaluate?: boolean) => { + const handleOnSelectModel = (model: any) => { const item = model || {}; - onSelectModel(item, evaluate); + if (item.evaluateResult && !item.isGGUF) { + onSelectModelAfterEvaluate(item); + } else { + onSelectModel(item); + } setCurrent(item.id); currentRef.current = item.id; }; @@ -332,12 +338,13 @@ const SearchModel: React.FC = (props) => { repoOptions: resultList }; }); + // current selected item const currentItem = resultList.find( (item) => item.id === currentRef.current ); if (currentItem) { - handleOnSelectModel(currentItem, true); + onSelectModelAfterEvaluate(currentItem); } } catch (error) { if (requestIdRef.current === currentRequestId) { @@ -406,17 +413,6 @@ const SearchModel: React.FC = (props) => { sortType: sort }); - // It's a new request, so we need to reset the state - unlockWarningStatus?.(); - displayEvaluateStatus?.( - { - show: list?.length > 0, - message: '' - }, - { - override: true - } - ); handleOnSelectModel(list[0]); setLoadingModel?.(false); @@ -498,18 +494,6 @@ const SearchModel: React.FC = (props) => { repoOptions: currentList }; }); - unlockWarningStatus?.(); - // reset evaluate status - displayEvaluateStatus?.( - { - show: true, - message: '' - }, - { - override: true - } - ); - console.log('isEvaluating:', isEvaluating); handleOnSelectModel(currentList[0]); handleEvaluate(currentList); } else if (modelSource === modelSourceMap.modelscope_value) { diff --git a/src/pages/llmodels/hooks/index.ts b/src/pages/llmodels/hooks/index.ts index 060a6413..3f459eb0 100644 --- a/src/pages/llmodels/hooks/index.ts +++ b/src/pages/llmodels/hooks/index.ts @@ -530,6 +530,10 @@ export const useCheckCompatibility = () => { return noLocalValue || noOllamaValue; }; + const clearCahceFormValues = () => { + cacheFormValuesRef.current = {}; + }; + const handleOnValuesChange = async (params: { changedValues: any; allValues: any; @@ -537,6 +541,7 @@ export const useCheckCompatibility = () => { }) => { const { allValues, source } = params; if (_.isEqual(cacheFormValuesRef.current, allValues)) { + console.log('No changes detected, skipping evaluation.'); return; } @@ -604,6 +609,7 @@ export const useCheckCompatibility = () => { handleBackendChangeBefore, handleOnValuesChange: debounceHandleValuesChange, handleEvaluateOnChange: handleOnValuesChange, + clearCahceFormValues, warningStatus, checkTokenRef, submitAnyway diff --git a/src/pages/playground/components/message-input.tsx b/src/pages/playground/components/message-input.tsx index ac751f5b..2cbeb242 100644 --- a/src/pages/playground/components/message-input.tsx +++ b/src/pages/playground/components/message-input.tsx @@ -305,6 +305,7 @@ const MessageInput: React.FC = forwardRef( uid: updateUidCount(), format: audioTypeMap[data.file.type] as AudioFormat, base64: base64Audio.split(',')[1], + name: audioData.name, data: _.pick(audioData, ['url', 'name', 'duration']) } ] @@ -518,6 +519,7 @@ const MessageInput: React.FC = forwardRef( diff --git a/src/pages/playground/components/multiple-chat/message-body.tsx b/src/pages/playground/components/multiple-chat/message-body.tsx index 892d4668..e70fd1f4 100644 --- a/src/pages/playground/components/multiple-chat/message-body.tsx +++ b/src/pages/playground/components/multiple-chat/message-body.tsx @@ -217,6 +217,7 @@ const MessageBody: React.FC = forwardRef( @@ -247,6 +248,7 @@ const MessageBody: React.FC = forwardRef( diff --git a/src/pages/playground/config/types.ts b/src/pages/playground/config/types.ts index bc86b594..fa36b3e6 100644 --- a/src/pages/playground/config/types.ts +++ b/src/pages/playground/config/types.ts @@ -19,6 +19,7 @@ export interface AudioData { uid: string | number; base64: string; format: AudioFormat; + name?: string; data: { url: string; name: string;