From 005295ac97718605d4e3448db70e11347990cc3f Mon Sep 17 00:00:00 2001 From: jialin Date: Thu, 16 Oct 2025 17:57:28 +0800 Subject: [PATCH] style: gguf model ux --- src/assets/images/external-link.png | Bin 0 -> 1336 bytes src/components/status-tag/index.tsx | 2 +- src/locales/en-US/backends.ts | 3 +- src/locales/ja-JP/backends.ts | 3 +- src/locales/ru-RU/backends.ts | 3 +- src/locales/zh-CN/backends.ts | 3 +- .../_components/column-wrapper/style.less | 1 + src/pages/backends/forms/version-info.tsx | 2 +- .../llmodels/components/deploy-modal.tsx | 137 ++++++++++++++---- .../llmodels/components/hf-model-file.tsx | 7 +- src/pages/llmodels/components/model-card.tsx | 4 +- .../llmodels/components/search-model.tsx | 9 +- .../llmodels/components/title-wrapper.tsx | 4 +- src/pages/llmodels/config/index.ts | 18 ++- src/pages/llmodels/config/types.ts | 2 - src/pages/llmodels/download/index.tsx | 30 ++-- .../llmodels/forms/local-path-source.tsx | 11 +- src/pages/llmodels/forms/online-source.tsx | 48 +++--- src/pages/llmodels/style/model-card.less | 5 + 19 files changed, 203 insertions(+), 89 deletions(-) create mode 100644 src/assets/images/external-link.png diff --git a/src/assets/images/external-link.png b/src/assets/images/external-link.png new file mode 100644 index 0000000000000000000000000000000000000000..8ee1205982f0a2a3285aa08cec9cc02b264a2477 GIT binary patch literal 1336 zcmX|BeK^wz9RL1)+dPa#b@Q@QR&+u+^O6WR(o85s;*c;ehv-FZXSrX+Jvx#+nTf1B zoF&Ov>S)D)G z`dS(iSi*EAV%6QU76LtV?ThtLOxZhP2%)E{tALrH=Cq+tBhUW1C`fx}WYJ}LJ!*zK&F3wLm=Dq^_C(Iw3bPNxyS7(*0Qvz@9=RCgY1K2xmVc~RY zTk??Sn#tp~%eK3e41Nj!n1+!mhYwF*-J%Mru>`)xE4V8cwY^t+HyV1l4<4Plq?jsw zb3kkaW+vBW!x5QcSCT#)o~wG!y5TG+&I0=Ct}sHSlVE^)E5TGO!(nD8zso=WHe5BY z?x>$z4TFv09sNdL`H72q-Q4CjvyDrUN^{RR$E4-;nbp5m%5oe8ij7E&dx1!MsHvbx zVP50}^}j{jaMTEU(5J%S_p;ng0v;Z$#oO4b%a>!VEW_ zy`Y5v)~5O6(%LixBhJP7e)cN0S&PcuwgW`WFTlere3(ligRs%8uYBhS3S?XJ+|NxZ z)wE9qTyN{oIjj3+pB&qLle6W~Xv}hz^Vj90a)g;R5f)*OD;niedx+RkmDzkY%1tdS z0JgK4184ZG1+%pG}E7)t6t-6de zcOEk$v;tFc3higq$>>ov}{X&<^IP4UEKF=z0;HHlvM{*<+?6e$cCCaRh%*Y?6 zT3P*x0L^yfL-JdO6%(x~6QypI1d!f|^iO#2wz+>^HP$zkh=K#nq*>n3;qk|->^xD} zSvk<1rQL^HOyr4MSn+tMqJ0=?>9|4SJPAkm_Dmfl(9^e_9A60?dat4yVDd^* zBNwIe_euK5AR&`@I{{Bi4Jkuo&NX??5HTv_wDm412;RhuMhrTIK`bH`bXTj_22L0| z)%hcK+vQXa0XtO1D76HJ&1<*YkgMBss7f%t(x_7N;9qgy+1l9M z5=ED}E)NF3F^FI0m_c?2rY1)TK2NlsKMIiJFxg|N>tA))3(`XzHpFmB-;3O`&elk= zDtEy4#L_udyJkYC6HdG2Ov6l{*=$PT3gJF{D8i^N!nWh^`vmRRs|1u(p_q8R@RZzQaQ3r5g< z?r2j3WW;vX+vD3ej9;j?feK^VWj(~sk7N0Y2$l?~^;VDukM_2rF-^;xOEvlc8co0s g$Ihkw)A~4kIAA1s8no*1otGH!-L}KK!7Jv(f3h`ACjbBd literal 0 HcmV?d00001 diff --git a/src/components/status-tag/index.tsx b/src/components/status-tag/index.tsx index a3cef232..997dc979 100644 --- a/src/components/status-tag/index.tsx +++ b/src/components/status-tag/index.tsx @@ -79,7 +79,7 @@ const StatusTag: React.FC = ({ if (link) { return statusValue.message?.replace( linkReg, - '$2' + `$2` ); } return statusValue.message; diff --git a/src/locales/en-US/backends.ts b/src/locales/en-US/backends.ts index d003c5d6..bfda00db 100644 --- a/src/locales/en-US/backends.ts +++ b/src/locales/en-US/backends.ts @@ -23,5 +23,6 @@ export default { 'backend.form.versionConfig': 'Versions Config', 'backend.form.addParameter': 'Add Parameter', 'backend.form.noVersion': 'No versions added', - 'backend.noVersion': 'No versions found' + 'backend.noVersion': 'No versions found', + 'backend.versionInfo.autoImage': 'Automatically selected at runtime' }; diff --git a/src/locales/ja-JP/backends.ts b/src/locales/ja-JP/backends.ts index d003c5d6..bfda00db 100644 --- a/src/locales/ja-JP/backends.ts +++ b/src/locales/ja-JP/backends.ts @@ -23,5 +23,6 @@ export default { 'backend.form.versionConfig': 'Versions Config', 'backend.form.addParameter': 'Add Parameter', 'backend.form.noVersion': 'No versions added', - 'backend.noVersion': 'No versions found' + 'backend.noVersion': 'No versions found', + 'backend.versionInfo.autoImage': 'Automatically selected at runtime' }; diff --git a/src/locales/ru-RU/backends.ts b/src/locales/ru-RU/backends.ts index d003c5d6..bfda00db 100644 --- a/src/locales/ru-RU/backends.ts +++ b/src/locales/ru-RU/backends.ts @@ -23,5 +23,6 @@ export default { 'backend.form.versionConfig': 'Versions Config', 'backend.form.addParameter': 'Add Parameter', 'backend.form.noVersion': 'No versions added', - 'backend.noVersion': 'No versions found' + 'backend.noVersion': 'No versions found', + 'backend.versionInfo.autoImage': 'Automatically selected at runtime' }; diff --git a/src/locales/zh-CN/backends.ts b/src/locales/zh-CN/backends.ts index fe37561d..6c4d78d2 100644 --- a/src/locales/zh-CN/backends.ts +++ b/src/locales/zh-CN/backends.ts @@ -23,5 +23,6 @@ export default { 'backend.form.versionConfig': '版本配置', 'backend.form.addParameter': '添加参数', 'backend.form.noVersion': '未添加版本', - 'backend.noVersion': '未找到版本' + 'backend.noVersion': '未找到版本', + 'backend.versionInfo.autoImage': '运行时自动选择' }; diff --git a/src/pages/_components/column-wrapper/style.less b/src/pages/_components/column-wrapper/style.less index 6774f907..5770f24f 100644 --- a/src/pages/_components/column-wrapper/style.less +++ b/src/pages/_components/column-wrapper/style.less @@ -14,6 +14,7 @@ flex: 1; display: flex; position: relative; + width: 100%; .column-wrapper { border-left: none; diff --git a/src/pages/backends/forms/version-info.tsx b/src/pages/backends/forms/version-info.tsx index a2757027..f5682f05 100644 --- a/src/pages/backends/forms/version-info.tsx +++ b/src/pages/backends/forms/version-info.tsx @@ -108,7 +108,7 @@ export const VersionItem: React.FC = ({ data }) => { {data.is_built_in - ? 'Selected dynamically at runtime' + ? intl.formatMessage({ id: 'backend.versionInfo.autoImage' }) : data.image_name} diff --git a/src/pages/llmodels/components/deploy-modal.tsx b/src/pages/llmodels/components/deploy-modal.tsx index 65acbb82..b19f914b 100644 --- a/src/pages/llmodels/components/deploy-modal.tsx +++ b/src/pages/llmodels/components/deploy-modal.tsx @@ -1,11 +1,14 @@ +import { getRequestId } from '@/atoms/models'; import ModalFooter from '@/components/modal-footer'; import GSDrawer from '@/components/scroller-modal/gs-drawer'; import { PageActionType } from '@/config/types'; +import useDeferredRequest from '@/hooks/use-deferred-request'; import { ProviderValueMap } from '@/pages/cluster-management/config'; import { useIntl } from '@umijs/max'; +import { useMemoizedFn } from 'ahooks'; import { Button } from 'antd'; import _ from 'lodash'; -import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { FC, useEffect, useMemo, useRef, useState } from 'react'; import styled from 'styled-components'; import ColumnWrapper from '../../_components/column-wrapper'; import { defaultFormValues, deployFormKeyMap, modelSourceMap } from '../config'; @@ -20,14 +23,21 @@ import { } from '../hooks'; import useCheckBackend from '../hooks/use-check-backend'; import CompatibilityAlert from './compatible-alert'; -import GGUFResult from './gguf-result'; +import HFModelFile from './hf-model-file'; import ModelCard from './model-card'; import SearchModel from './search-model'; import Separator from './separator'; import TitleWrapper from './title-wrapper'; const pickFieldsFromSpec = ['backend_version', 'backend_parameters', 'env']; -const dropFieldsFromForm = ['name', 'file_name', 'repo_id', 'backend']; +const dropFieldsFromForm = [ + 'name', + 'huggingface_filename', + 'model_scope_file_path', + 'model_scope_model_id', + 'huggingface_repo_id', + 'backend' +]; const resetFields = ['worker_selector', 'env']; const ModalFooterStyle = { @@ -127,6 +137,11 @@ const AddModal: FC = (props) => { const requestModelIdRef = useRef(0); const currentSelectedModel = useRef({}); + const { run: fetchModelFiles } = useDeferredRequest( + () => modelFileRef.current?.fetchModelFiles?.(), + 100 + ); + const updateSelectedModel = (model: any) => { currentSelectedModel.current = model; setSelectedModel(model); @@ -182,6 +197,78 @@ const AddModal: FC = (props) => { return categories || null; }; + const { run: onSelectFile } = useDeferredRequest( + async (item: any, modelInfo: any, manual?: boolean) => { + unlockWarningStatus(); + + const evaluateRes = await handleOnValuesChangeBefore?.({ + changedValues: {}, + allValues: form.current?.form?.getFieldsValue?.(), + source: props.source + }); + console.log('onSelectFile:', item, modelInfo, evaluateRes); + + // for cancel evaluate request case + if (!evaluateRes) { + return; + } + + const defaultSpec = getDefaultSpec({ + evaluateResult: evaluateRes + }); + + /** + * do not reset backend_parameters when select a model file + */ + const formValues = form.current?.getFieldsValue?.(pickFieldsFromSpec); + + form.current?.setFieldsValue?.({ + ..._.omit(modelInfo, ['name']), + huggingface_filename: item.fakeName, + model_scope_file_path: item.fakeName, + backend_parameters: + formValues.backend_parameters?.length > 0 + ? formValues.backend_parameters + : defaultSpec.backend_parameters || [], + backend_version: + formValues.backend_version || defaultSpec.backend_version, + env: formValues.env || defaultSpec.env, + categories: getCategory(item) + }); + }, + 100 + ); + + const handleSelectModelFile = async ( + item: any, + options: { requestModelId: number; manual?: boolean } + ) => { + const { requestModelId, manual } = options || {}; + if (requestModelId !== getRequestId()) { + return; + } + console.log('handleSelectModelFile:', item, selectedModel); + + const modelInfo = onSelectModel(selectedModel, props.source); + + form.current?.setFieldsValue?.({ + ..._.omit(modelInfo, ['name']), + huggingface_filename: item.fakeName, + model_scope_file_path: item.fakeName, + backend_parameters: [], + backend_version: '', + backend: '', + env: {}, + categories: getCategory(item) + }); + + // evaluate the form data when select a model file + // TODO: reset backend related fields when select a GGUF file + if (item.fakeName) { + // onSelectFile(item, modelInfo, manual); + } + }; + const handleCancelFiles = () => { cancelEvaluate(); modelFileRef.current?.cancelRequest(); @@ -242,8 +329,7 @@ const AddModal: FC = (props) => { }; if (item.isGGUF) { - warningStatus.type = 'danger'; - warningStatus.message = 'GGUF model is not supported.'; + fetchModelFiles(); } setWarningStatus(warningStatus, { override: true }); }; @@ -317,25 +403,21 @@ const AddModal: FC = (props) => { }; const handleBackendChange = async (backend: string) => { - setIsGGUF(false); - const data = form.current.form.getFieldsValue?.(); const res = handleBackendChangeBefore(data); if (res.show) { return; } if (data.local_path || props.source !== modelSourceMap.local_path_value) { - // TODO confirm wheather it is gguf by model file not by backend + // TODO confirm whether it is gguf by model file not by backend handleOnValuesChange?.({ changedValues: {}, - // allValues: - // backend === backendOptionsMap.llamaBox - // ? data - // : _.omit(data, [ - // 'cpu_offloading', - // 'distributed_inference_across_workers' - // ]), - allValues: data, + allValues: isGGUF + ? data + : _.omit(data, [ + 'cpu_offloading', + 'distributed_inference_across_workers' + ]), source: props.source }); } @@ -349,9 +431,9 @@ const AddModal: FC = (props) => { }); }; - const handleCancel = useCallback(() => { + const handleCancel = useMemoizedFn(() => { onCancel?.(); - }, [onCancel]); + }); const initClusterId = () => { if (initialValues?.cluster_id) { @@ -492,7 +574,7 @@ const AddModal: FC = (props) => { - + = (props) => { modelSource={props.source} setIsGGUF={handleSetIsGGUF} > - {isGGUF && } + + {isGGUF && ( + + )} @@ -532,11 +623,7 @@ const AddModal: FC = (props) => { showOkBtn={!showExtraButton} extra={ showExtraButton && ( -