Merge pull request #4470 from seefs001/feature/show-removed-upstream-models
feat: show removed upstream models in fetch models modal
This commit is contained in:
@@ -269,6 +269,24 @@ const EditChannelModal = (props) => {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}, [inputs.model_mapping]);
|
}, [inputs.model_mapping]);
|
||||||
|
const redirectModelKeyList = useMemo(() => {
|
||||||
|
const mapping = inputs.model_mapping;
|
||||||
|
if (typeof mapping !== 'string') return [];
|
||||||
|
const trimmed = mapping.trim();
|
||||||
|
if (!trimmed) return [];
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(trimmed);
|
||||||
|
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const keys = Object.keys(parsed)
|
||||||
|
.map((key) => key.trim())
|
||||||
|
.filter((key) => key);
|
||||||
|
return Array.from(new Set(keys));
|
||||||
|
} catch (error) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}, [inputs.model_mapping]);
|
||||||
const upstreamDetectedModels = useMemo(
|
const upstreamDetectedModels = useMemo(
|
||||||
() =>
|
() =>
|
||||||
Array.from(
|
Array.from(
|
||||||
@@ -3842,6 +3860,7 @@ const EditChannelModal = (props) => {
|
|||||||
models={fetchedModels}
|
models={fetchedModels}
|
||||||
selected={inputs.models}
|
selected={inputs.models}
|
||||||
redirectModels={redirectModelList}
|
redirectModels={redirectModelList}
|
||||||
|
redirectSourceModels={redirectModelKeyList}
|
||||||
onConfirm={(selectedModels) => {
|
onConfirm={(selectedModels) => {
|
||||||
handleInputChange('models', selectedModels);
|
handleInputChange('models', selectedModels);
|
||||||
showSuccess(t('模型列表已更新'));
|
showSuccess(t('模型列表已更新'));
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ const ModelSelectModal = ({
|
|||||||
models = [],
|
models = [],
|
||||||
selected = [],
|
selected = [],
|
||||||
redirectModels = [],
|
redirectModels = [],
|
||||||
|
redirectSourceModels = [],
|
||||||
onConfirm,
|
onConfirm,
|
||||||
onCancel,
|
onCancel,
|
||||||
}) => {
|
}) => {
|
||||||
@@ -54,6 +55,14 @@ const ModelSelectModal = ({
|
|||||||
if (typeof model === 'object' && model.model_name) return model.model_name;
|
if (typeof model === 'object' && model.model_name) return model.model_name;
|
||||||
return String(model ?? '');
|
return String(model ?? '');
|
||||||
};
|
};
|
||||||
|
const normalizeModelList = (modelList = []) =>
|
||||||
|
Array.from(
|
||||||
|
new Set(
|
||||||
|
(modelList || [])
|
||||||
|
.map((model) => getModelName(model).trim())
|
||||||
|
.filter(Boolean),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
const normalizedSelected = useMemo(
|
const normalizedSelected = useMemo(
|
||||||
() => (selected || []).map(getModelName),
|
() => (selected || []).map(getModelName),
|
||||||
@@ -78,6 +87,10 @@ const ModelSelectModal = ({
|
|||||||
),
|
),
|
||||||
[redirectModels],
|
[redirectModels],
|
||||||
);
|
);
|
||||||
|
const normalizedRedirectSourceSet = useMemo(
|
||||||
|
() => new Set(normalizeModelList(redirectSourceModels)),
|
||||||
|
[redirectSourceModels],
|
||||||
|
);
|
||||||
const normalizedSelectedSet = useMemo(() => {
|
const normalizedSelectedSet = useMemo(() => {
|
||||||
const set = new Set();
|
const set = new Set();
|
||||||
(selected || []).forEach((model) => {
|
(selected || []).forEach((model) => {
|
||||||
@@ -116,6 +129,16 @@ const ModelSelectModal = ({
|
|||||||
const existingModels = filteredModels.filter((model) =>
|
const existingModels = filteredModels.filter((model) =>
|
||||||
isExistingModel(model),
|
isExistingModel(model),
|
||||||
);
|
);
|
||||||
|
const fetchedModelSet = useMemo(
|
||||||
|
() => new Set(normalizeModelList(models)),
|
||||||
|
[models],
|
||||||
|
);
|
||||||
|
const removedModels = normalizeModelList(selected).filter(
|
||||||
|
(model) =>
|
||||||
|
!fetchedModelSet.has(model) &&
|
||||||
|
!normalizedRedirectSourceSet.has(model) &&
|
||||||
|
model.toLowerCase().includes(keyword.toLowerCase()),
|
||||||
|
);
|
||||||
|
|
||||||
// 同步外部选中值
|
// 同步外部选中值
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -127,11 +150,15 @@ const ModelSelectModal = ({
|
|||||||
// 当模型列表变化时,设置默认tab
|
// 当模型列表变化时,设置默认tab
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
// 默认显示新获取模型tab,如果没有新模型则显示已有模型
|
if (newModels.length > 0) {
|
||||||
const hasNewModels = newModels.length > 0;
|
setActiveTab('new');
|
||||||
setActiveTab(hasNewModels ? 'new' : 'existing');
|
} else if (removedModels.length > 0) {
|
||||||
|
setActiveTab('removed');
|
||||||
|
} else {
|
||||||
|
setActiveTab('existing');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [visible, newModels.length, selected]);
|
}, [visible, newModels.length, removedModels.length, selected]);
|
||||||
|
|
||||||
const handleOk = () => {
|
const handleOk = () => {
|
||||||
onConfirm && onConfirm(checkedList);
|
onConfirm && onConfirm(checkedList);
|
||||||
@@ -197,6 +224,14 @@ const ModelSelectModal = ({
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
|
...(removedModels.length > 0
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
tab: `${t('上游已删除的模型')} (${removedModels.length})`,
|
||||||
|
itemKey: 'removed',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
];
|
];
|
||||||
|
|
||||||
// 处理分类全选/取消全选
|
// 处理分类全选/取消全选
|
||||||
@@ -343,9 +378,11 @@ const ModelSelectModal = ({
|
|||||||
showClear
|
showClear
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Spin spinning={!models || models.length === 0}>
|
<Spin
|
||||||
|
spinning={!models || (models.length === 0 && removedModels.length === 0)}
|
||||||
|
>
|
||||||
<div style={{ maxHeight: 400, overflowY: 'auto', paddingRight: 8 }}>
|
<div style={{ maxHeight: 400, overflowY: 'auto', paddingRight: 8 }}>
|
||||||
{filteredModels.length === 0 ? (
|
{filteredModels.length === 0 && removedModels.length === 0 ? (
|
||||||
<Empty
|
<Empty
|
||||||
image={
|
image={
|
||||||
<IllustrationNoResult style={{ width: 150, height: 150 }} />
|
<IllustrationNoResult style={{ width: 150, height: 150 }} />
|
||||||
@@ -369,6 +406,14 @@ const ModelSelectModal = ({
|
|||||||
{renderModelsByCategory(existingModelsByCategory, 'existing')}
|
{renderModelsByCategory(existingModelsByCategory, 'existing')}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{activeTab === 'removed' && removedModels.length > 0 && (
|
||||||
|
<div>
|
||||||
|
{renderModelsByCategory(
|
||||||
|
categorizeModels(removedModels),
|
||||||
|
'removed',
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</Checkbox.Group>
|
</Checkbox.Group>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -382,7 +427,11 @@ const ModelSelectModal = ({
|
|||||||
<div className='flex items-center justify-end gap-2'>
|
<div className='flex items-center justify-end gap-2'>
|
||||||
{(() => {
|
{(() => {
|
||||||
const currentModels =
|
const currentModels =
|
||||||
activeTab === 'new' ? newModels : existingModels;
|
activeTab === 'new'
|
||||||
|
? newModels
|
||||||
|
: activeTab === 'removed'
|
||||||
|
? removedModels
|
||||||
|
: existingModels;
|
||||||
const currentSelected = currentModels.filter((model) =>
|
const currentSelected = currentModels.filter((model) =>
|
||||||
checkedList.includes(model),
|
checkedList.includes(model),
|
||||||
).length;
|
).length;
|
||||||
|
|||||||
Reference in New Issue
Block a user