diff --git a/src/pages/llmodels/config/types.ts b/src/pages/llmodels/config/types.ts index 6579e193..d517ecfd 100644 --- a/src/pages/llmodels/config/types.ts +++ b/src/pages/llmodels/config/types.ts @@ -422,7 +422,8 @@ export interface InstanceRestartCount { export interface ModelLoraAdapterResult { lora_list: Array<{ + is_local: boolean; lora_repo_name: string; - source: string; + source: 'huggingface' | 'model_scope' | 'local_path'; }>; } diff --git a/src/pages/llmodels/forms/lora-list-item.tsx b/src/pages/llmodels/forms/lora-list-item.tsx index 09e4cc17..f95fe950 100644 --- a/src/pages/llmodels/forms/lora-list-item.tsx +++ b/src/pages/llmodels/forms/lora-list-item.tsx @@ -12,13 +12,17 @@ import useQueryModelLoraList, { import loraSelectionStyles from '../style/lora-selection.less'; interface LoraListItemProps { - item: { value: any[]; lora_name: string }; + item: { value: any[]; lora_name: string; source: string }; base: string; defaultDataList: LoraOptionGroup[]; selectedRepoNames: Set; duplicateNames: Set; validated: boolean; - onChange: (partial: { value?: any[]; lora_name?: string }) => void; + onChange: (partial: { + value?: any[]; + lora_name?: string; + source?: string; + }) => void; } const LoraListItem: React.FC = ({ @@ -77,8 +81,9 @@ const LoraListItem: React.FC = ({ debouncedSearch(q); }; - const handleCascaderChange = (value: any) => { - onChange({ value: value || [] }); + const handleCascaderChange = (value: any, selectedOptions?: any[]) => { + const leaf = selectedOptions?.[selectedOptions.length - 1]; + onChange({ value: value || [], source: leaf?.source || '' }); }; const handleNameChange = (e: React.ChangeEvent) => { @@ -97,19 +102,18 @@ const LoraListItem: React.FC = ({ validated && (nameEmpty || isDuplicate) ? ('error' as const) : 'success'; const displayRender = (labels: any[]) => { + const left = + typeof labels[0] === 'string' ? labels[0].replace(/\/+$/, '') : labels[0]; + const right = + typeof labels[1] === 'string' ? labels[1].replace(/^\/+/, '') : labels[1]; + const content = ( + + {left}/{right} + + ); return ( - - {labels[0]} / {labels[1]} - - } - > - - {labels[0]} / {labels[1]} - + + {content} ); }; diff --git a/src/pages/llmodels/forms/model-lora-list.tsx b/src/pages/llmodels/forms/model-lora-list.tsx index aa0041e7..381e69ab 100644 --- a/src/pages/llmodels/forms/model-lora-list.tsx +++ b/src/pages/llmodels/forms/model-lora-list.tsx @@ -6,7 +6,7 @@ import { FormData, LoraListItem } from '../config/types'; import useQueryModelLoraList from '../services/use-query-lora-list'; import LoraItem from './lora-list-item'; -type ItemValue = { value: any[]; lora_name: string }; +type ItemValue = { value: any[]; lora_name: string; source: string }; const ModelLoraList = () => { const intl = useIntl(); @@ -37,7 +37,8 @@ const ModelLoraList = () => { it.source && it.lora_repo_name ? [it.source, it.lora_repo_name] : [], - lora_name: it.lora_name || '' + lora_name: it.lora_name || '', + source: it.source || '' })) ); } @@ -56,6 +57,30 @@ const ModelLoraList = () => { prevBaseRef.current = base; }, [base]); + useEffect(() => { + if (!defaultDataList.length) return; + const groupByRepo: Record = {}; + defaultDataList.forEach((group) => { + group.children.forEach((child) => { + groupByRepo[child.value] = group.value; + }); + }); + setItemList((prev) => { + let changed = false; + const next = prev.map((it) => { + const repo = it.value?.[1]; + if (!repo) return it; + const groupValue = groupByRepo[repo]; + if (groupValue && groupValue !== it.value[0]) { + changed = true; + return { ...it, value: [groupValue, repo] }; + } + return it; + }); + return changed ? next : prev; + }); + }, [defaultDataList]); + const selectedRepoNames = useMemo(() => { return new Set( itemList.map((it) => it.value?.[1]).filter(Boolean) as string[] @@ -78,7 +103,9 @@ const ModelLoraList = () => { const syncFormField = (newItemList: ItemValue[]) => { const newFormList = newItemList .map((it) => ({ - source: (it.value?.[0] || '') as 'huggingface' | 'model_scope', + source: (it.source || it.value?.[0] || '') as + | 'huggingface' + | 'model_scope', lora_repo_name: it.value?.[1] || '', lora_name: it.lora_name || '' })) @@ -103,7 +130,7 @@ const ModelLoraList = () => { }; const handleAdd = () => { - const newItemList = [...itemList, { value: [], lora_name: '' }]; + const newItemList = [...itemList, { value: [], lora_name: '', source: '' }]; setItemList(newItemList); syncFormField(newItemList); }; diff --git a/src/pages/llmodels/services/use-query-lora-list.ts b/src/pages/llmodels/services/use-query-lora-list.ts index 75ddaecf..8ad0c7e8 100644 --- a/src/pages/llmodels/services/use-query-lora-list.ts +++ b/src/pages/llmodels/services/use-query-lora-list.ts @@ -31,6 +31,9 @@ const sourceLabel = (source: string) => { if (source === modelSourceMap.modelscope_value) { return modelSourceMap.modelScope; } + if (source === modelSourceMap.local_path_value) { + return modelSourceMap.local_path; + } return source; }; @@ -53,15 +56,19 @@ export const useQueryModelLoraList = () => { if (result) { const groups: Record = {}; result.lora_list.forEach((item) => { - if (!groups[item.source]) { - groups[item.source] = { - label: sourceLabel(item.source), - value: item.source, + const groupKey = + item.is_local || item.source === modelSourceMap.local_path_value + ? modelSourceMap.local_path_value + : item.source; + if (!groups[groupKey]) { + groups[groupKey] = { + label: sourceLabel(groupKey), + value: groupKey, isParent: true, children: [] }; } - groups[item.source].children.push({ + groups[groupKey].children.push({ label: item.lora_repo_name, value: item.lora_repo_name, source: item.source, diff --git a/src/pages/llmodels/style/lora-selection.less b/src/pages/llmodels/style/lora-selection.less index 0df3e40c..1f334aef 100644 --- a/src/pages/llmodels/style/lora-selection.less +++ b/src/pages/llmodels/style/lora-selection.less @@ -10,6 +10,10 @@ .ant-cascader-menus { display: grid; grid-template-columns: 140px 1fr; + + .ant-cascader-menu:only-child { + grid-column: 1 / -1; + } } } } diff --git a/src/pages/model-routes/forms/targets.tsx b/src/pages/model-routes/forms/targets.tsx index 03460836..2acc448d 100644 --- a/src/pages/model-routes/forms/targets.tsx +++ b/src/pages/model-routes/forms/targets.tsx @@ -24,18 +24,10 @@ import useTargetSourceModels from '../hooks/use-target-source-models'; const OptionWrapper = styled.span` display: flex; align-items: center; - gap: 8px; + gap: 4px; .lora-tag { - display: inline-flex; - align-items: center; - padding: 0 7px; - height: 18px; - font-size: 11px; - line-height: 18px; - border: 1px solid var(--ant-color-split); - border-radius: 12px; + font-size: 12px; color: var(--ant-color-text-tertiary); - background-color: transparent; } `; @@ -230,7 +222,7 @@ const TargetsForm = forwardRef((props, ref) => { return ( {data.label} - LoRA + [LoRA] ); }