diff --git a/src/locales/en-US/resources.ts b/src/locales/en-US/resources.ts index 73abd7f5..465c9907 100644 --- a/src/locales/en-US/resources.ts +++ b/src/locales/en-US/resources.ts @@ -67,9 +67,10 @@ export default { 'The default storage directory is /var/lib/gpustack/cache or the directory specified with --data-dir.', 'resources.modelfiles.retry.download': 'Retry Download', 'resources.modelfiles.storagePath.holder': - 'Waiting for download to complete...', + 'Waiting for the download to complete...', 'resources.filter.worker': 'Filter by Worker', 'resources.filter.source': 'Filter by Source', 'resources.modelfiles.delete.tips': 'Also delete the file from disk!', - 'resources.modelfiles.copy.tips': 'Copy Full Path' + 'resources.modelfiles.copy.tips': 'Copy Full Path', + 'resources.filter.path': 'Filter by Path' }; diff --git a/src/locales/ja-JP/resources.ts b/src/locales/ja-JP/resources.ts index a6910c27..e0afeb28 100644 --- a/src/locales/ja-JP/resources.ts +++ b/src/locales/ja-JP/resources.ts @@ -72,7 +72,8 @@ export default { 'resources.filter.worker': 'Filter by Worker', 'resources.filter.source': 'Filter by Source', 'resources.modelfiles.delete.tips': 'Also delete the file from disk!', - 'resources.modelfiles.copy.tips': 'Copy Full Path' + 'resources.modelfiles.copy.tips': 'Copy Full Path', + 'resources.filter.path': 'Filter by Path' }; // ========== To-Do: Translate Keys (Remove After Translation) ========== @@ -91,4 +92,5 @@ export default { // 13. 'resources.worker.add.step1', // 14. 'resources.worker.add.step2.tips', // 15. 'resources.modelfiles.copy.tips', +// 16. 'resources.filter.path', // ========== End of To-Do List ========== diff --git a/src/locales/ru-RU/resources.ts b/src/locales/ru-RU/resources.ts index ab26a27d..875c10e8 100644 --- a/src/locales/ru-RU/resources.ts +++ b/src/locales/ru-RU/resources.ts @@ -13,10 +13,14 @@ export default { 'resources.form.enablePartialOffload': 'Разрешить оффлоуд на CPU', 'resources.form.placementStrategy': 'Стратегия размещения', 'resources.form.workerSelector': 'Селектор воркеров', - 'resources.form.enableDistributedInferenceAcrossWorkers': 'Разрешить распределённый инференс между воркерами', - 'resources.form.spread.tips': 'Равномерно распределяет ресурсы между воркерами. Может увеличить фрагментацию ресурсов на отдельных воркерах.', - 'resources.form.binpack.tips': 'Максимизирует утилизацию ресурсов, уменьшая фрагментацию на воркерах/GPU.', - 'resources.form.workerSelector.description': 'Система выбирает подходящие GPU/воркеры для развертывания моделей на основе меток.', + 'resources.form.enableDistributedInferenceAcrossWorkers': + 'Разрешить распределённый инференс между воркерами', + 'resources.form.spread.tips': + 'Равномерно распределяет ресурсы между воркерами. Может увеличить фрагментацию ресурсов на отдельных воркерах.', + 'resources.form.binpack.tips': + 'Максимизирует утилизацию ресурсов, уменьшая фрагментацию на воркерах/GPU.', + 'resources.form.workerSelector.description': + 'Система выбирает подходящие GPU/воркеры для развертывания моделей на основе меток.', 'resources.table.ip': 'IP-адрес', 'resources.table.cpu': 'CPU', 'resources.table.memory': 'ОЗУ', @@ -37,14 +41,19 @@ export default { 'resources.table.wokers': 'воркеры', 'resources.worker.linuxormaxos': 'Linux или macOS', 'resources.table.unified': 'Объединённая память', - 'resources.worker.add.step1': 'Получить токен (Запустить на сервере)', + 'resources.worker.add.step1': + 'Получить токен (Запустить на сервере)', 'resources.worker.add.step2': 'Зарегистрировать воркер', - 'resources.worker.add.step2.tips': 'Примечание: выполнить на подключаемом воркере, mytoken это токен, полученный на первом шаге', - 'resources.worker.add.step3': 'После успешной регистрации обновите список воркеров.', + 'resources.worker.add.step2.tips': + 'Примечание: выполнить на подключаемом воркере, mytoken это токен, полученный на первом шаге', + 'resources.worker.add.step3': + 'После успешной регистрации обновите список воркеров.', 'resources.worker.container.supported': 'Только для Linux.', 'resources.worker.current.version': 'Текущая версия: {version}', - 'resources.worker.driver.install': 'Установите необходимые драйверы и библиотеки перед установкой GPUStack.', - 'resources.worker.select.command': 'Выберите метку для генерации команды и скопируйте её.', + 'resources.worker.driver.install': + 'Установите необходимые драйверы и библиотеки перед установкой GPUStack.', + 'resources.worker.select.command': + 'Выберите метку для генерации команды и скопируйте её.', 'resources.worker.script.install': 'Установка скриптом', 'resources.worker.container.install': 'Установка контейнером (только Linux)', 'resources.worker.cann.tips': `Укажите индексы GPU через ASCEND_VISIBLE_DEVICES=0,1,2,3 или ASCEND_VISIBLE_DEVICES=0-3.`, @@ -54,15 +63,17 @@ export default { 'resources.modelfiles.size': 'Размер', 'resources.modelfiles.selecttarget': 'Выбрать назначение', 'resources.modelfiles.form.localdir': 'Локальный каталог', - 'resources.modelfiles.form.localdir.tips': 'Каталог по умолчанию --data-dir.', + 'resources.modelfiles.form.localdir.tips': + 'Каталог по умолчанию --data-dir.', 'resources.modelfiles.retry.download': 'Повторить загрузку', 'resources.modelfiles.storagePath.holder': 'Ожидание завершения загрузки...', 'resources.filter.worker': 'Фильтровать по узлу', 'resources.filter.source': 'Фильтровать по источнику', 'resources.modelfiles.delete.tips': 'Также удалить файл с диска!', - 'resources.modelfiles.copy.tips': 'Скопировать полный путь' + 'resources.modelfiles.copy.tips': 'Скопировать полный путь', + 'resources.filter.path': 'Filter by Path' }; // ========== To-Do: Translate Keys (Remove After Translation) ========== - +// 1. 'resources.filter.path' // ========== End of To-Do List ========== diff --git a/src/locales/zh-CN/resources.ts b/src/locales/zh-CN/resources.ts index cbb17457..8fd977ac 100644 --- a/src/locales/zh-CN/resources.ts +++ b/src/locales/zh-CN/resources.ts @@ -69,5 +69,6 @@ export default { 'resources.filter.worker': '按 worker 筛选', 'resources.filter.source': '按来源筛选', 'resources.modelfiles.delete.tips': '同时从磁盘删除文件!', - 'resources.modelfiles.copy.tips': '复制完整路径' + 'resources.modelfiles.copy.tips': '复制完整路径', + 'resources.filter.path': '路径查询' }; diff --git a/src/pages/llmodels/components/deploy-builtin-modal.tsx b/src/pages/llmodels/components/deploy-builtin-modal.tsx index 2d4084bc..b957c5ad 100644 --- a/src/pages/llmodels/components/deploy-builtin-modal.tsx +++ b/src/pages/llmodels/components/deploy-builtin-modal.tsx @@ -83,6 +83,8 @@ const AddModal: React.FC = (props) => { setWarningStatus, handleEvaluate, generateGPUIds, + cancelEvaluate, + submitAnyway, handleOnValuesChange, warningStatus } = useCheckCompatibility(); @@ -99,7 +101,6 @@ const AddModal: React.FC = (props) => { const axiosToken = useRef(null); const selectSpecRef = useRef({} as CatalogSpec); const specListRef = useRef([]); - const submitAnyway = useRef(null); const handleSumit = () => { form.current?.submit?.(); @@ -111,9 +112,11 @@ const AddModal: React.FC = (props) => { }; const generateSubmitData = (formData: FormData) => { + const gpuSelector = generateGPUIds(formData); const data = { ..._.omit(selectSpecRef.current, ['name']), - ...formData + ...formData, + ...gpuSelector }; return data; @@ -233,6 +236,12 @@ const AddModal: React.FC = (props) => { return evalutionData; }; + const handleCheckFormData = () => { + const values = form.current?.getFieldsValue(); + const allValues = generateSubmitData(values); + handleCheckCompatibility(allValues); + }; + const handleSourceChange = (source: string) => { const defaultSpec = _.get(sourceGroupMap.current, `${source}.0`, {}); initFormDataBySource(defaultSpec); @@ -245,8 +254,7 @@ const AddModal: React.FC = (props) => { }); // set form value initFormDataBySource(defaultSpec); - const data = generateSubmitData(form.current.getFieldsValue()); - handleCheckCompatibility(data); + handleCheckFormData(); }; const checkSize = (list: any[]) => { @@ -314,7 +322,7 @@ const AddModal: React.FC = (props) => { form.current.setFieldsValue({ ...data }); - handleCheckCompatibility(data); + handleCheckFormData(); }; const fetchSpecData = async () => { @@ -364,12 +372,14 @@ const AddModal: React.FC = (props) => { 'name', _.toLower(current.name).replace(/\s/g, '-') || '' ); - if (defaultSpec.backend === backendOptionsMap.vllm) { - setIsGGUF(false); - } + + const allValues = generateSubmitData(defaultSpec); + handleCheckCompatibility(allValues); if (defaultSpec.backend === backendOptionsMap.llamaBox) { setIsGGUF(true); + } else { + setIsGGUF(false); } console.log('values====', form.current.form.getFieldsValue()); } catch (error) { @@ -386,8 +396,7 @@ const AddModal: React.FC = (props) => { form.current.setFieldsValue({ ...data }); - const allValues = generateSubmitData(data); - handleCheckCompatibility(allValues); + handleCheckFormData(); }; const handleOnSizeChange = (val: number) => { @@ -408,21 +417,12 @@ const AddModal: React.FC = (props) => { form.current.setFieldsValue({ ...data }); - const allValues = generateSubmitData(data); - handleCheckCompatibility(allValues); + handleCheckFormData(); }; const handleOk = async (values: FormData) => { const data = generateSubmitData(values); - - if (submitAnyway.current) { - onOk(data); - return; - } - const evaluateResult = await handleCheckCompatibility(data); - if (evaluateResult?.compatible) { - onOk(data); - } + onOk(data); }; const handleCancel = useCallback(() => { @@ -436,6 +436,7 @@ const AddModal: React.FC = (props) => { } return () => { axiosToken.current?.cancel?.(); + cancelEvaluate(); setWarningStatus({ show: false, title: '', diff --git a/src/pages/llmodels/components/deploy-modal.tsx b/src/pages/llmodels/components/deploy-modal.tsx index 98cdf455..3943b6dc 100644 --- a/src/pages/llmodels/components/deploy-modal.tsx +++ b/src/pages/llmodels/components/deploy-modal.tsx @@ -117,16 +117,7 @@ const AddModal: FC = (props) => { const handleOnOk = async (allValues: FormData) => { const result = getSourceRepoConfigValue(props.source, allValues).values; - if (submitAnyway.current) { - onOk(result); - return; - } - - const evalutionData = await handleEvaluate(result); - handleShowCompatibleAlert?.(evalutionData); - if (evalutionData?.compatible) { - onOk(result); - } + onOk(result); }; const handleSubmitAnyway = async () => { @@ -198,22 +189,25 @@ const AddModal: FC = (props) => { onCancel?.(); }, [onCancel]); - useEffect(() => { - if (!open) { - return; - } + const handleOnOpen = useCallback(() => { if (props.deploymentType === 'modelFiles') { form.current?.form?.setFieldsValue({ ...props.initialValues }); - setIsGGUF(props.isGGUF || false); } else { const backend = source === modelSourceMap.ollama_library_value ? backendOptionsMap.llamaBox : backendOptionsMap.vllm; form.current?.setFieldValue?.('backend', backend); - setIsGGUF(backend === backendOptionsMap.llamaBox); + } + }, [source, props.initialValues, props.deploymentType]); + + useEffect(() => { + if (!open) { + return; + } else { + handleOnOpen(); } return () => { @@ -225,7 +219,7 @@ const AddModal: FC = (props) => { }); checkTokenRef.current?.cancel(); }; - }, [open, props.isGGUF, source, props.initialValues, props.deploymentType]); + }, [open, handleOnOpen]); return ( = ({ - + {dayjs(instanceData.created_at).format('YYYY-MM-DD HH:mm:ss')} diff --git a/src/pages/llmodels/components/search-model.tsx b/src/pages/llmodels/components/search-model.tsx index cf5574a9..a0f5ef61 100644 --- a/src/pages/llmodels/components/search-model.tsx +++ b/src/pages/llmodels/components/search-model.tsx @@ -123,7 +123,7 @@ const SearchModel: React.FC = (props) => { value: item.name, label: item.name, isGGUF: checkIsGGUF(item), - source: modelSourceMap.modelscope_value + source: modelSource }; }); return list; @@ -161,7 +161,7 @@ const SearchModel: React.FC = (props) => { tags: item.Tags, libraries: item.Libraries, isGGUF: checkIsGGUF({ tags: item.Tags, libraries: item.Libraries }), - source: modelSourceMap.modelscope_value + source: modelSource }; }); diff --git a/src/pages/llmodels/components/table-list.tsx b/src/pages/llmodels/components/table-list.tsx index 3e632c58..202a9a66 100644 --- a/src/pages/llmodels/components/table-list.tsx +++ b/src/pages/llmodels/components/table-list.tsx @@ -182,11 +182,13 @@ const Models: React.FC = ({ hasLinuxWorker?: boolean; source: SourceType; gpuOptions: any[]; + isGGUF?: boolean; modelFileOptions?: any[]; }>({ show: false, hasLinuxWorker: false, width: 600, + isGGUF: false, source: modelSourceMap.huggingface_value as SourceType, gpuOptions: [], modelFileOptions: [] @@ -830,6 +832,7 @@ const Models: React.FC = ({ title={intl.formatMessage({ id: 'models.button.deploy' })} source={openDeployModal.source} width={openDeployModal.width} + isGGUF={openDeployModal.isGGUF} hasLinuxWorker={openDeployModal.hasLinuxWorker} gpuOptions={openDeployModal.gpuOptions} modelFileOptions={openDeployModal.modelFileOptions || []} diff --git a/src/pages/llmodels/components/update-modal.tsx b/src/pages/llmodels/components/update-modal.tsx index dac0c027..6fa88b18 100644 --- a/src/pages/llmodels/components/update-modal.tsx +++ b/src/pages/llmodels/components/update-modal.tsx @@ -311,23 +311,12 @@ const UpdateModal: React.FC = (props) => { submitData = { ..._.omit(formdata, ['scheduleType']), categories: formdata.categories ? [formdata.categories] : [], - worker_selector: null, + worker_selector: + formdata.scheduleType === 'manual' ? null : formdata.worker_selector, ...obj, - ...(formdata.scheduleType === 'manual' - ? generateGPUIds(formdata) - : { gpu_selector: null }) + ...generateGPUIds(formdata) }; - - if (submitAnyway.current) { - onOk(submitData); - return; - } - - const evalutionData = await handleEvaluate(submitData); - handleShowCompatibleAlert?.(evalutionData); - if (evalutionData?.compatible) { - onOk(submitData); - } + onOk(submitData); }; const onValuesChange = (changedValues: any, allValues: any) => { diff --git a/src/pages/llmodels/config/button-actions.ts b/src/pages/llmodels/config/button-actions.ts index a9f46104..10299bb3 100644 --- a/src/pages/llmodels/config/button-actions.ts +++ b/src/pages/llmodels/config/button-actions.ts @@ -29,7 +29,7 @@ const icons = { export const modalConfig: Record< string, - { show: boolean; width: string | number; source: any } + { show: boolean; width: string | number; source: any; isGGUF?: boolean } > = { huggingface: { show: true, @@ -39,6 +39,7 @@ export const modalConfig: Record< ollama_library: { show: true, width: 600, + isGGUF: true, source: modelSourceMap.ollama_library_value }, modelscope: { diff --git a/src/pages/llmodels/hooks/index.ts b/src/pages/llmodels/hooks/index.ts index 8b81c094..420cc273 100644 --- a/src/pages/llmodels/hooks/index.ts +++ b/src/pages/llmodels/hooks/index.ts @@ -413,6 +413,11 @@ export const useCheckCompatibility = () => { const debounceHandleValuesChange = _.debounce(handleOnValuesChange, 300); + const cancelEvaluate = () => { + checkTokenRef.current?.cancel(); + checkTokenRef.current = null; + }; + useEffect(() => { return () => { checkTokenRef.current?.cancel(); @@ -429,6 +434,7 @@ export const useCheckCompatibility = () => { submitAnyway, generateGPUIds, handleEvaluate, - setWarningStatus + setWarningStatus, + cancelEvaluate }; }; diff --git a/src/pages/resources/components/model-files.tsx b/src/pages/resources/components/model-files.tsx index d35f8c00..9911739c 100644 --- a/src/pages/resources/components/model-files.tsx +++ b/src/pages/resources/components/model-files.tsx @@ -527,7 +527,7 @@ const ModelFiles = () => {