Refactor(setting-model): Refactor the model management interface and optimize the component structure. #10703 (#10905)

### What problem does this PR solve?

Refactor(setting-model): Refactor the model management interface and
optimize the component structure. #10703

### Type of change

- [x] Refactoring
This commit is contained in:
chanx
2025-10-31 09:27:30 +08:00
committed by GitHub
parent ff2365b146
commit 5a830ea68b
11 changed files with 1191 additions and 338 deletions

View File

@ -1,47 +1,16 @@
import { ReactComponent as MoreModelIcon } from '@/assets/svg/more-model.svg';
import { LlmIcon } from '@/components/svg-icon';
import { useTheme } from '@/components/theme-provider';
import { LLMFactory } from '@/constants/llm';
import { useSetModalState, useTranslate } from '@/hooks/common-hooks';
import {
LlmItem,
useFetchMyLlmListDetailed,
useSelectLlmList,
} from '@/hooks/llm-hooks';
import { getRealModelName } from '@/utils/llm-util';
import {
CloseCircleOutlined,
EditOutlined,
SettingOutlined,
} from '@ant-design/icons';
import {
Button,
Card,
Col,
Collapse,
CollapseProps,
Divider,
Flex,
List,
Row,
Space,
Spin,
Tag,
Tooltip,
Typography,
} from 'antd';
import { CircleHelp } from 'lucide-react';
import { LlmItem, useFetchMyLlmListDetailed } from '@/hooks/llm-hooks';
import { useCallback, useMemo } from 'react';
import SettingTitle from '../components/setting-title';
import { isLocalLlmFactory } from '../utils';
import ApiKeyModal from './api-key-modal';
import AzureOpenAIModal from './azure-openai-modal';
import BedrockModal from './bedrock-modal';
import SystemSetting from './components/system-setting';
import { AvailableModels } from './components/un-add-model';
import { UsedModel } from './components/used-model';
import FishAudioModal from './fish-audio-modal';
import GoogleModal from './google-modal';
import {
useHandleDeleteFactory,
useHandleDeleteLlm,
useSubmitApiKey,
useSubmitAzure,
useSubmitBedrock,
@ -56,164 +25,15 @@ import {
useSubmityiyan,
} from './hooks';
import HunyuanModal from './hunyuan-modal';
import styles from './index.less';
import TencentCloudModal from './next-tencent-modal';
import OllamaModal from './ollama-modal';
import SparkModal from './spark-modal';
import SystemModelSettingModal from './system-model-setting-modal';
import VolcEngineModal from './volcengine-modal';
import YiyanModal from './yiyan-modal';
const { Text } = Typography;
interface IModelCardProps {
item: LlmItem;
clickApiKey: (llmFactory: string) => void;
handleEditModel: (model: any, factory: LlmItem) => void;
}
type TagType =
| 'LLM'
| 'TEXT EMBEDDING'
| 'TEXT RE-RANK'
| 'TTS'
| 'SPEECH2TEXT'
| 'IMAGE2TEXT'
| 'MODERATION';
const sortTags = (tags: string) => {
const orderMap: Record<TagType, number> = {
LLM: 1,
'TEXT EMBEDDING': 2,
'TEXT RE-RANK': 3,
TTS: 4,
SPEECH2TEXT: 5,
IMAGE2TEXT: 6,
MODERATION: 7,
};
return tags
.split(',')
.map((tag) => tag.trim())
.sort(
(a, b) =>
(orderMap[a as TagType] || 999) - (orderMap[b as TagType] || 999),
);
};
const ModelCard = ({ item, clickApiKey, handleEditModel }: IModelCardProps) => {
const { visible, switchVisible } = useSetModalState();
const { t } = useTranslate('setting');
const { theme } = useTheme();
const { handleDeleteLlm } = useHandleDeleteLlm(item.name);
const { handleDeleteFactory } = useHandleDeleteFactory(item.name);
const handleApiKeyClick = () => {
clickApiKey(item.name);
};
const handleShowMoreClick = () => {
switchVisible();
};
return (
<List.Item>
<Card
className={theme === 'dark' ? styles.addedCardDark : styles.addedCard}
>
<Row align={'middle'}>
<Col span={12}>
<Flex gap={'middle'} align="center">
<LlmIcon name={item.name} />
<Flex vertical gap={'small'}>
<b>{item.name}</b>
<Flex wrap="wrap">
{sortTags(item.tags).map((tag, index) => (
<Tag
key={index}
style={{
fontSize: '12px',
margin: '1px',
paddingInline: '4px',
}}
>
{tag}
</Tag>
))}
</Flex>
</Flex>
</Flex>
</Col>
<Col span={12} className={styles.factoryOperationWrapper}>
<Space size={'middle'}>
<Button onClick={handleApiKeyClick}>
<Flex align="center" gap={4}>
{isLocalLlmFactory(item.name) ||
item.name === LLMFactory.VolcEngine ||
item.name === LLMFactory.TencentHunYuan ||
item.name === LLMFactory.XunFeiSpark ||
item.name === LLMFactory.BaiduYiYan ||
item.name === LLMFactory.FishAudio ||
item.name === LLMFactory.TencentCloud ||
item.name === LLMFactory.GoogleCloud ||
item.name === LLMFactory.AzureOpenAI
? t('addTheModel')
: 'API-Key'}
<SettingOutlined />
</Flex>
</Button>
<Button onClick={handleShowMoreClick}>
<Flex align="center" gap={4}>
{visible ? t('hideModels') : t('showMoreModels')}
<MoreModelIcon />
</Flex>
</Button>
<Button type={'text'} onClick={handleDeleteFactory}>
<Flex align="center">
<CloseCircleOutlined style={{ color: '#D92D20' }} />
</Flex>
</Button>
</Space>
</Col>
</Row>
{visible && (
<List
size="small"
dataSource={item.llm}
className={styles.llmList}
renderItem={(model) => (
<List.Item>
<Space>
{getRealModelName(model.name)}
<Tag color="#b8b8b8">{model.type}</Tag>
{isLocalLlmFactory(item.name) && (
<Tooltip title={t('edit', { keyPrefix: 'common' })}>
<Button
type={'text'}
onClick={() => handleEditModel(model, item)}
>
<EditOutlined style={{ color: '#1890ff' }} />
</Button>
</Tooltip>
)}
<Tooltip title={t('delete', { keyPrefix: 'common' })}>
<Button type={'text'} onClick={handleDeleteLlm(model.name)}>
<CloseCircleOutlined style={{ color: '#D92D20' }} />
</Button>
</Tooltip>
</Space>
</List.Item>
)}
/>
)}
</Card>
</List.Item>
);
};
const UserSettingModel = () => {
const { factoryList, myLlmList: llmList, loading } = useSelectLlmList();
const ModelProviders = () => {
const { saveSystemModelSettingLoading, onSystemSettingSavingOk } =
useSubmitSystemModelSetting();
const { data: detailedLlmList } = useFetchMyLlmListDetailed();
const { theme } = useTheme();
const {
saveApiKeyLoading,
initialApiKey,
@ -224,14 +44,6 @@ const UserSettingModel = () => {
hideApiKeyModal,
showApiKeyModal,
} = useSubmitApiKey();
const {
saveSystemModelSettingLoading,
onSystemSettingSavingOk,
systemSettingVisible,
hideSystemSettingModal,
showSystemSettingModal,
} = useSubmitSystemModelSetting();
const { t } = useTranslate('setting');
const {
llmAddingVisible,
hideLlmAddingModal,
@ -342,6 +154,7 @@ const UserSettingModel = () => {
const handleAddModel = useCallback(
(llmFactory: string) => {
console.log('handleAddModel', llmFactory);
if (isLocalLlmFactory(llmFactory)) {
showLlmAddingModal(llmFactory);
} else if (llmFactory in ModalMap) {
@ -378,116 +191,21 @@ const UserSettingModel = () => {
},
[showApiKeyModal, showLlmAddingModal, ModalMap, detailedLlmList],
);
const items: CollapseProps['items'] = [
{
key: '1',
label: t('addedModels'),
children: (
<List
grid={{ gutter: 16, column: 1 }}
dataSource={llmList}
renderItem={(item) => (
<ModelCard
item={item}
clickApiKey={handleAddModel}
handleEditModel={handleEditModel}
></ModelCard>
)}
/>
),
},
{
key: '2',
label: (
<div className="flex items-center gap-2">
{t('modelsToBeAdded')}
<Tooltip title={t('modelsToBeAddedTooltip')}>
<CircleHelp className="size-4" />
</Tooltip>
</div>
),
children: (
<List
grid={{
gutter: {
xs: 8,
sm: 10,
md: 12,
lg: 16,
xl: 20,
xxl: 24,
},
xs: 1,
sm: 1,
md: 2,
lg: 3,
xl: 4,
xxl: 8,
}}
dataSource={factoryList}
renderItem={(item) => (
<List.Item>
<Card
className={
theme === 'dark'
? styles.toBeAddedCardDark
: styles.toBeAddedCard
}
>
<Flex vertical gap={'middle'}>
<LlmIcon name={item.name} imgClass="h-12 w-auto" />
<Flex vertical gap={'middle'}>
<b>
<Text ellipsis={{ tooltip: item.name }}>{item.name}</Text>
</b>
<Flex wrap="wrap" style={{ minHeight: '50px' }}>
{sortTags(item.tags).map((tag, index) => (
<Tag
key={index}
style={{
fontSize: '8px',
margin: '1px',
paddingInline: '4px',
height: '22px',
}}
>
{tag}
</Tag>
))}
</Flex>
</Flex>
</Flex>
<Divider className={styles.modelDivider}></Divider>
<Button
type="link"
onClick={() => handleAddModel(item.name)}
className={styles.addButton}
>
{t('addTheModel')}
</Button>
</Card>
</List.Item>
)}
/>
),
},
];
return (
<section id="xx" className="w-full space-y-6">
<Spin spinning={loading}>
<section className={styles.modelContainer}>
<SettingTitle
title={t('model')}
description={t('modelDescription')}
showRightButton
clickButton={showSystemSettingModal}
></SettingTitle>
<Divider></Divider>
<Collapse defaultActiveKey={['1', '2']} ghost items={items} />
</section>
</Spin>
<div className="flex w-full">
<section className="flex flex-col gap-4 w-3/5 px-5 border-r border-border-button overflow-auto scrollbar-auto">
<SystemSetting
onOk={onSystemSettingSavingOk}
loading={saveSystemModelSettingLoading}
/>
<UsedModel
handleAddModel={handleAddModel}
handleEditModel={handleEditModel}
/>
</section>
<section className="flex flex-col w-2/5 overflow-auto scrollbar-auto">
<AvailableModels handleAddModel={handleAddModel} />
</section>
<ApiKeyModal
visible={apiKeyVisible}
hideModal={hideApiKeyModal}
@ -497,14 +215,6 @@ const UserSettingModel = () => {
onOk={onApiKeySavingOk}
llmFactory={llmFactory}
></ApiKeyModal>
{systemSettingVisible && (
<SystemModelSettingModal
visible={systemSettingVisible}
onOk={onSystemSettingSavingOk}
hideModal={hideSystemSettingModal}
loading={saveSystemModelSettingLoading}
></SystemModelSettingModal>
)}
<OllamaModal
visible={llmAddingVisible}
hideModal={hideLlmAddingModal}
@ -577,8 +287,7 @@ const UserSettingModel = () => {
loading={AzureAddingLoading}
llmFactory={LLMFactory.AzureOpenAI}
></AzureOpenAIModal>
</section>
</div>
);
};
export default UserSettingModel;
export default ModelProviders;