feat: 重定向后的模型视为已有的模型,附带特殊提示
This commit is contained in:
parent
d6939d2f64
commit
3fc9182dd4
@ -190,6 +190,30 @@ const EditChannelModal = (props) => {
|
|||||||
const [keyMode, setKeyMode] = useState('append'); // 密钥模式:replace(覆盖)或 append(追加)
|
const [keyMode, setKeyMode] = useState('append'); // 密钥模式:replace(覆盖)或 append(追加)
|
||||||
const [isEnterpriseAccount, setIsEnterpriseAccount] = useState(false); // 是否为企业账户
|
const [isEnterpriseAccount, setIsEnterpriseAccount] = useState(false); // 是否为企业账户
|
||||||
const [doubaoApiEditUnlocked, setDoubaoApiEditUnlocked] = useState(false); // 豆包渠道自定义 API 地址隐藏入口
|
const [doubaoApiEditUnlocked, setDoubaoApiEditUnlocked] = useState(false); // 豆包渠道自定义 API 地址隐藏入口
|
||||||
|
const redirectModelList = 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 values = Object.values(parsed)
|
||||||
|
.map((value) =>
|
||||||
|
typeof value === 'string' ? value.trim() : undefined,
|
||||||
|
)
|
||||||
|
.filter((value) => value);
|
||||||
|
return Array.from(new Set(values));
|
||||||
|
} catch (error) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}, [inputs.model_mapping]);
|
||||||
|
|
||||||
// 密钥显示状态
|
// 密钥显示状态
|
||||||
const [keyDisplayState, setKeyDisplayState] = useState({
|
const [keyDisplayState, setKeyDisplayState] = useState({
|
||||||
@ -3044,6 +3068,7 @@ const EditChannelModal = (props) => {
|
|||||||
visible={modelModalVisible}
|
visible={modelModalVisible}
|
||||||
models={fetchedModels}
|
models={fetchedModels}
|
||||||
selected={inputs.models}
|
selected={inputs.models}
|
||||||
|
redirectModels={redirectModelList}
|
||||||
onConfirm={(selectedModels) => {
|
onConfirm={(selectedModels) => {
|
||||||
handleInputChange('models', selectedModels);
|
handleInputChange('models', selectedModels);
|
||||||
showSuccess(t('模型列表已更新'));
|
showSuccess(t('模型列表已更新'));
|
||||||
|
|||||||
@ -17,7 +17,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
For commercial licensing, please contact support@quantumnous.com
|
For commercial licensing, please contact support@quantumnous.com
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect, useMemo } from 'react';
|
||||||
import { useIsMobile } from '../../../../hooks/common/useIsMobile';
|
import { useIsMobile } from '../../../../hooks/common/useIsMobile';
|
||||||
import {
|
import {
|
||||||
Modal,
|
Modal,
|
||||||
@ -28,12 +28,13 @@ import {
|
|||||||
Empty,
|
Empty,
|
||||||
Tabs,
|
Tabs,
|
||||||
Collapse,
|
Collapse,
|
||||||
|
Tooltip,
|
||||||
} from '@douyinfe/semi-ui';
|
} from '@douyinfe/semi-ui';
|
||||||
import {
|
import {
|
||||||
IllustrationNoResult,
|
IllustrationNoResult,
|
||||||
IllustrationNoResultDark,
|
IllustrationNoResultDark,
|
||||||
} from '@douyinfe/semi-illustrations';
|
} from '@douyinfe/semi-illustrations';
|
||||||
import { IconSearch } from '@douyinfe/semi-icons';
|
import { IconSearch, IconInfoCircle } from '@douyinfe/semi-icons';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { getModelCategories } from '../../../../helpers/render';
|
import { getModelCategories } from '../../../../helpers/render';
|
||||||
|
|
||||||
@ -41,6 +42,7 @@ const ModelSelectModal = ({
|
|||||||
visible,
|
visible,
|
||||||
models = [],
|
models = [],
|
||||||
selected = [],
|
selected = [],
|
||||||
|
redirectModels = [],
|
||||||
onConfirm,
|
onConfirm,
|
||||||
onCancel,
|
onCancel,
|
||||||
}) => {
|
}) => {
|
||||||
@ -50,15 +52,54 @@ const ModelSelectModal = ({
|
|||||||
const [activeTab, setActiveTab] = useState('new');
|
const [activeTab, setActiveTab] = useState('new');
|
||||||
|
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
|
const normalizeModelName = (model) =>
|
||||||
|
typeof model === 'string' ? model.trim() : '';
|
||||||
|
const normalizedRedirectModels = useMemo(
|
||||||
|
() =>
|
||||||
|
Array.from(
|
||||||
|
new Set(
|
||||||
|
(redirectModels || [])
|
||||||
|
.map((model) => normalizeModelName(model))
|
||||||
|
.filter(Boolean),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
[redirectModels],
|
||||||
|
);
|
||||||
|
const normalizedSelectedSet = useMemo(() => {
|
||||||
|
const set = new Set();
|
||||||
|
(selected || []).forEach((model) => {
|
||||||
|
const normalized = normalizeModelName(model);
|
||||||
|
if (normalized) {
|
||||||
|
set.add(normalized);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return set;
|
||||||
|
}, [selected]);
|
||||||
|
const classificationSet = useMemo(() => {
|
||||||
|
const set = new Set(normalizedSelectedSet);
|
||||||
|
normalizedRedirectModels.forEach((model) => set.add(model));
|
||||||
|
return set;
|
||||||
|
}, [normalizedSelectedSet, normalizedRedirectModels]);
|
||||||
|
const redirectOnlySet = useMemo(() => {
|
||||||
|
const set = new Set();
|
||||||
|
normalizedRedirectModels.forEach((model) => {
|
||||||
|
if (!normalizedSelectedSet.has(model)) {
|
||||||
|
set.add(model);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return set;
|
||||||
|
}, [normalizedRedirectModels, normalizedSelectedSet]);
|
||||||
|
|
||||||
const filteredModels = models.filter((m) =>
|
const filteredModels = models.filter((m) =>
|
||||||
m.toLowerCase().includes(keyword.toLowerCase()),
|
String(m || '').toLowerCase().includes(keyword.toLowerCase()),
|
||||||
);
|
);
|
||||||
|
|
||||||
// 分类模型:新获取的模型和已有模型
|
// 分类模型:新获取的模型和已有模型
|
||||||
const newModels = filteredModels.filter((model) => !selected.includes(model));
|
const isExistingModel = (model) =>
|
||||||
|
classificationSet.has(normalizeModelName(model));
|
||||||
|
const newModels = filteredModels.filter((model) => !isExistingModel(model));
|
||||||
const existingModels = filteredModels.filter((model) =>
|
const existingModels = filteredModels.filter((model) =>
|
||||||
selected.includes(model),
|
isExistingModel(model),
|
||||||
);
|
);
|
||||||
|
|
||||||
// 同步外部选中值
|
// 同步外部选中值
|
||||||
@ -228,7 +269,20 @@ const ModelSelectModal = ({
|
|||||||
<div className='grid grid-cols-2 gap-x-4'>
|
<div className='grid grid-cols-2 gap-x-4'>
|
||||||
{categoryData.models.map((model) => (
|
{categoryData.models.map((model) => (
|
||||||
<Checkbox key={model} value={model} className='my-1'>
|
<Checkbox key={model} value={model} className='my-1'>
|
||||||
{model}
|
<span className='flex items-center gap-2'>
|
||||||
|
<span>{model}</span>
|
||||||
|
{redirectOnlySet.has(normalizeModelName(model)) && (
|
||||||
|
<Tooltip
|
||||||
|
position='top'
|
||||||
|
content={t('来自模型重定向,尚未加入模型列表')}
|
||||||
|
>
|
||||||
|
<IconInfoCircle
|
||||||
|
size='small'
|
||||||
|
className='text-amber-500 cursor-help'
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user