feat(settings): refactor LLM model management UI and components

This commit is contained in:
guangfei.zhao
2025-10-21 18:21:48 +08:00
parent bcfcc4b40a
commit 4784fcb23f
7 changed files with 225 additions and 360 deletions
+1 -1
View File
@@ -1 +1 @@
<svg id="_层_2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 274.37 172.76"><defs><style>.cls-2{fill:#36cfd1}.cls-3{fill:#624aff}</style></defs><g id="_层_1-2"><path class="cls-3" d="M24.78 73.55h25.65V99.2H24.78zm99.14 25.66h25.65v25.65h-25.65zm76.95 25.65h-25.65v22.19h47.84V99.21h-22.19v25.65z"/><path class="cls-2" d="M149.57 73.55h25.65V99.2h-25.65zM24.78 47.9h25.65v25.65H24.78z"/><path class="cls-3" d="M223.06 73.55h25.65V99.2h-25.65z"/><path class="cls-2" d="M223.06 47.9h25.65v25.65h-25.65z"/><path class="cls-3" d="M175.22 25.71V47.9h25.65v25.65h22.19V25.71h-47.84z"/><path class="cls-2" d="M98.27 73.55h25.65V99.2H98.27z"/><path class="cls-3" d="M72.62 47.9h25.65V25.71H50.43v47.84h22.19V47.9zm0 51.31H50.43v47.84h47.84v-22.19H72.62V99.21z"/><path style="fill:none" d="M0 0h274.37v172.76H0z"/></g></svg> <svg id="layer_2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 274.37 172.76"><defs><style>.cls-2{fill:#36cfd1}.cls-3{fill:#624aff}</style></defs><g id="layer_1-2"><path class="cls-3" d="M24.78 73.55h25.65V99.2H24.78zm99.14 25.66h25.65v25.65h-25.65zm76.95 25.65h-25.65v22.19h47.84V99.21h-22.19v25.65z"/><path class="cls-2" d="M149.57 73.55h25.65V99.2h-25.65zM24.78 47.9h25.65v25.65H24.78z"/><path class="cls-3" d="M223.06 73.55h25.65V99.2h-25.65z"/><path class="cls-2" d="M223.06 47.9h25.65v25.65h-25.65z"/><path class="cls-3" d="M175.22 25.71V47.9h25.65v25.65h22.19V25.71h-47.84z"/><path class="cls-2" d="M98.27 73.55h25.65V99.2H98.27z"/><path class="cls-3" d="M72.62 47.9h25.65V25.71H50.43v47.84h22.19V47.9zm0 51.31H50.43v47.84h47.84v-22.19H72.62V99.21z"/><path style="fill:none" d="M0 0h274.37v172.76H0z"/></g></svg>

Before

Width:  |  Height:  |  Size: 821 B

After

Width:  |  Height:  |  Size: 822 B

+2 -2
View File
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<svg id="_图层_2" data-name="图层 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 111.08 53.12"> <svg id="layer_2" data-name="layer 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 111.08 53.12">
<defs> <defs>
<style> <style>
.cls-1 { .cls-1 {
@@ -7,7 +7,7 @@
} }
</style> </style>
</defs> </defs>
<g id="_图层_1-2" data-name="图层 1"> <g id="layer_1-2" data-name="layer 1">
<path class="cls-1" d="M106.25,0h-48.3c-2.67,0-4.83,2.16-4.83,4.83v14.49c0,2.67-2.16,4.83-4.83,4.83H4.83c-2.67,0-4.83,2.16-4.83,4.83v19.32c0,2.67,2.16,4.83,4.83,4.83h48.3c2.67,0,4.83-2.16,4.83-4.83v-14.49c0-2.67,2.16-4.83,4.83-4.83h43.47c2.67,0,4.83-2.16,4.83-4.83V4.83c0-2.67-2.16-4.83-4.83-4.83Z"/> <path class="cls-1" d="M106.25,0h-48.3c-2.67,0-4.83,2.16-4.83,4.83v14.49c0,2.67-2.16,4.83-4.83,4.83H4.83c-2.67,0-4.83,2.16-4.83,4.83v19.32c0,2.67,2.16,4.83,4.83,4.83h48.3c2.67,0,4.83-2.16,4.83-4.83v-14.49c0-2.67,2.16-4.83,4.83-4.83h43.47c2.67,0,4.83-2.16,4.83-4.83V4.83c0-2.67-2.16-4.83-4.83-4.83Z"/>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 595 B

After

Width:  |  Height:  |  Size: 589 B

+8 -8
View File
@@ -1,28 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<svg id="_图层_1" data-name="图层 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" <svg id="layer_1" data-name="layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 283.46 283.46"> viewBox="0 0 283.46 283.46">
<defs> <defs>
<style> <style>
.cls-1 { .cls-1 {
fill: url(#_未命名的渐变_5-2); fill: url(#gradient_5-2);
} }
.cls-2 { .cls-2 {
fill: url(#_未命名的渐变_9); fill: url(#gradient_9);
} }
.cls-3 { .cls-3 {
fill: url(#_未命名的渐变_5); fill: url(#gradient_5);
} }
</style> </style>
<linearGradient id="_未命名的渐变_5" data-name="未命名的渐变 5" x1="27.03" y1="287.05" x2="253.15" y2="1.14" <linearGradient id="gradient_5" data-name="gradient 5" x1="27.03" y1="287.05" x2="253.15" y2="1.14"
gradientUnits="userSpaceOnUse"> gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#e9a85e" /> <stop offset="0" stop-color="#e9a85e" />
<stop offset="1" stop-color="#f52b76" /> <stop offset="1" stop-color="#f52b76" />
</linearGradient> </linearGradient>
<linearGradient id="_未命名的渐变_5-2" data-name="未命名的渐变 5" x1="25.96" y1="286.21" x2="252.09" y2=".3" <linearGradient id="gradient_5-2" data-name="gradient 5" x1="25.96" y1="286.21" x2="252.09" y2=".3"
xlink:href="#_未命名的渐变_5" /> xlink:href="#gradient_5" />
<linearGradient id="_未命名的渐变_9" data-name="未命名的渐变 9" x1="-474.33" y1="476.58" x2="-160.37" y2="476.58" <linearGradient id="gradient_9" data-name="gradient 9" x1="-474.33" y1="476.58" x2="-160.37" y2="476.58"
gradientTransform="translate(669.07 -75.9) rotate(33.75)" gradientUnits="userSpaceOnUse"> gradientTransform="translate(669.07 -75.9) rotate(33.75)" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#6a0cf5" /> <stop offset="0" stop-color="#6a0cf5" />
<stop offset="1" stop-color="#ab66f3" /> <stop offset="1" stop-color="#ab66f3" />

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

+1 -1
View File
@@ -6,7 +6,7 @@ const svgPath = '/src/assets/svg'
interface AppSvgIconProps extends SvgIconProps { interface AppSvgIconProps extends SvgIconProps {
name: string; name: string;
point: 'llm' | 'chunk' | 'file' | 'default'; point?: 'llm' | 'chunk' | 'file' | 'default';
} }
const getPointPath = (point: AppSvgIconProps['point']) => { const getPointPath = (point: AppSvgIconProps['point']) => {
@@ -1,12 +1,100 @@
import { Box, Typography } from "@mui/material"; import {
Settings as SettingsIcon,
} from '@mui/icons-material'
import { LlmSvgIcon } from "@/components/AppSvgIcon";
import { IconMap, type LLMFactory } from "@/constants/llm";
import type { IFactory } from "@/interfaces/database/llm";
import { Box, Button, Card, CardContent, Chip, Typography } from "@mui/material";
import { useState } from "react";
function LLMFactoryCard() { // 模型类型标签颜色映射
return ( export const MODEL_TYPE_COLORS: Record<string, string> = {
<Box> 'LLM': '#1976d2',
<Typography variant="h6" gutterBottom> 'TEXT EMBEDDING': '#388e3c',
</Typography> 'TEXT RE-RANK': '#f57c00',
</Box> 'TTS': '#7b1fa2',
) 'SPEECH2TEXT': '#c2185b',
'IMAGE2TEXT': '#5d4037',
'MODERATION': '#455a64',
};
// 模型工厂卡片组件
interface ModelFactoryCardProps {
factory: IFactory;
onConfigure: (factory: IFactory) => void;
} }
export default LLMFactoryCard; const LLMFactoryCard: React.FC<ModelFactoryCardProps> = ({
factory,
onConfigure,
}) => {
// 获取工厂图标名称
const getFactoryIconName = (factoryName: LLMFactory) => {
return IconMap[factoryName] || 'default';
};
return (
<Card sx={{
mb: 2,
border: '1px solid #e0e0e0',
height: '100%',
display: 'flex',
flexDirection: 'column'
}}>
<CardContent sx={{
display: 'flex',
flexDirection: 'column',
alignItems: 'flex-start',
textAlign: 'left',
gap: 2,
flex: 1,
}}>
{/* 图标 */}
<LlmSvgIcon
name={getFactoryIconName(factory.name as LLMFactory)}
sx={{ width: 48, height: 48, color: 'primary.main' }}
/>
{/* 标题 */}
<Typography variant="h6" component="div" sx={{ fontWeight: 'bold' }}>
{factory.name}
</Typography>
{/* 标签 */}
<Box display="flex" flexWrap="wrap" gap={0.5} justifyContent="left">
{factory.model_types.map((type) => (
<Chip
key={type}
label={type.toUpperCase()}
size="small"
sx={{
backgroundColor: MODEL_TYPE_COLORS[type.toUpperCase()] || '#757575',
color: 'white',
fontSize: '0.65rem',
height: '20px',
}}
/>
))}
</Box>
{/* 配置按钮 */}
<Button
variant="text"
size="small"
onClick={() => onConfigure(factory)}
sx={{
mt: 'auto',
color: 'primary.main',
alignSelf: 'center',
width: 'fit-content'
}}
>
添加模型
</Button>
</CardContent>
</Card>
);
};
export default LLMFactoryCard
+107 -338
View File
@@ -9,266 +9,27 @@ import {
IconButton, IconButton,
Collapse, Collapse,
Grid, Grid,
Divider,
Dialog,
DialogTitle,
DialogContent,
DialogActions,
FormControl,
InputLabel,
Select,
MenuItem,
Alert, Alert,
CircularProgress, CircularProgress,
Tooltip, Tooltip,
Accordion,
AccordionSummary,
AccordionDetails,
Divider,
} from '@mui/material'; } from '@mui/material';
import { import {
ExpandMore as ExpandMoreIcon, ExpandMore as ExpandMoreIcon,
ExpandLess as ExpandLessIcon, ExpandLess as ExpandLessIcon,
Settings as SettingsIcon,
Delete as DeleteIcon,
Edit as EditIcon, Edit as EditIcon,
Add as AddIcon, Delete as DeleteIcon,
Visibility as VisibilityIcon,
VisibilityOff as VisibilityOffIcon,
} from '@mui/icons-material'; } from '@mui/icons-material';
import { useLlmModelSetting } from '@/hooks/setting-hooks'; import { useLlmModelSetting } from '@/hooks/setting-hooks';
import { useModelDialogs } from './hooks/useModelDialogs'; import { useModelDialogs } from './hooks/useModelDialogs';
import { LlmSvgIcon } from '@/components/AppSvgIcon'; import AppSvgIcon, { LlmSvgIcon } from '@/components/AppSvgIcon';
import { LLM_FACTORY_LIST, IconMap, type LLMFactory } from '@/constants/llm'; import { LLM_FACTORY_LIST, IconMap, type LLMFactory } from '@/constants/llm';
import type { IFactory, IMyLlmModel, ILlmItem } from '@/interfaces/database/llm'; import type { IFactory, IMyLlmModel, ILlmItem } from '@/interfaces/database/llm';
import LLMFactoryCard, { MODEL_TYPE_COLORS } from './components/LLMFactoryCard';
// 模型类型标签颜色映射
const MODEL_TYPE_COLORS: Record<string, string> = {
'LLM': '#1976d2',
'TEXT EMBEDDING': '#388e3c',
'TEXT RE-RANK': '#f57c00',
'TTS': '#7b1fa2',
'SPEECH2TEXT': '#c2185b',
'IMAGE2TEXT': '#5d4037',
'MODERATION': '#455a64',
};
// 模型工厂卡片组件
interface ModelFactoryCardProps {
factory: IFactory;
myModels: ILlmItem[];
onConfigure: (factory: IFactory) => void;
onDeleteFactory: (factoryName: string) => void;
onDeleteModel: (factoryName: string, modelName: string) => void;
onEditModel: (factory: IFactory, model: ILlmItem) => void;
}
const ModelFactoryCard: React.FC<ModelFactoryCardProps> = ({
factory,
myModels,
onConfigure,
onDeleteFactory,
onDeleteModel,
onEditModel,
}) => {
const [expanded, setExpanded] = useState(false);
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
const handleExpandClick = () => {
setExpanded(!expanded);
};
const handleDeleteFactory = () => {
onDeleteFactory(factory.name);
setShowDeleteConfirm(false);
};
// 获取工厂图标名称
const getFactoryIconName = (factoryName: LLMFactory) => {
return IconMap[factoryName] || 'default';
};
return (
<>
<Card sx={{ mb: 2, border: '1px solid #e0e0e0' }}>
<CardContent>
<Box display="flex" alignItems="center" justifyContent="space-between">
<Box display="flex" alignItems="center" gap={2}>
<LlmSvgIcon
name={getFactoryIconName(factory.name as LLMFactory)}
sx={{ width: 40, height: 40, color: 'primary.main' }}/>
<Box>
<Typography variant="h6" component="div">
{factory.name}
</Typography>
<Box display="flex" gap={1} mt={1}>
{factory.model_types.map((type) => (
<Chip
key={type}
label={type.toUpperCase()}
size="small"
sx={{
backgroundColor: MODEL_TYPE_COLORS[type.toUpperCase()] || '#757575',
color: 'white',
fontSize: '0.7rem',
}}
/>
))}
</Box>
</Box>
</Box>
<Box display="flex" alignItems="center" gap={1}>
<Button
variant="contained"
size="small"
startIcon={<SettingsIcon />}
onClick={() => onConfigure(factory)}
>
配置
</Button>
{myModels.length > 0 && (
<IconButton onClick={handleExpandClick}>
{expanded ? <ExpandLessIcon /> : <ExpandMoreIcon />}
</IconButton>
)}
<IconButton
color="error"
onClick={() => setShowDeleteConfirm(true)}
>
<DeleteIcon />
</IconButton>
</Box>
</Box>
<Collapse in={expanded} timeout="auto" unmountOnExit>
<Divider sx={{ my: 2 }} />
<Typography variant="subtitle2" gutterBottom>
已配置的模型 ({myModels.length})
</Typography>
<Grid container spacing={2}>
{myModels.map((model) => (
<Grid size={{ xs: 12, sm: 6, md: 4 }} key={model.name}>
<Card variant="outlined" sx={{ p: 2 }}>
<Box display="flex" justifyContent="space-between" alignItems="start">
<Box>
<Typography variant="body2" fontWeight="bold">
{model.name}
</Typography>
<Typography variant="caption" color="text.secondary">
{model.type}
</Typography>
<Typography variant="caption" display="block" color="text.secondary">
Max Tokens: {model.max_tokens}
</Typography>
<Typography variant="caption" display="block" color="text.secondary">
Used: {model.used_token}
</Typography>
</Box>
<Box>
<IconButton
size="small"
onClick={() => onEditModel(factory, model)}
>
<EditIcon fontSize="small" />
</IconButton>
<IconButton
size="small"
color="error"
onClick={() => onDeleteModel(factory.name, model.name)}
>
<DeleteIcon fontSize="small" />
</IconButton>
</Box>
</Box>
</Card>
</Grid>
))}
</Grid>
</Collapse>
</CardContent>
</Card>
{/* 删除确认对话框 */}
<Dialog open={showDeleteConfirm} onClose={() => setShowDeleteConfirm(false)}>
<DialogTitle>确认删除</DialogTitle>
<DialogContent>
<Typography>
确定要删除模型工厂 "{factory.name}" 吗?这将删除该工厂下的所有模型配置。
</Typography>
</DialogContent>
<DialogActions>
<Button onClick={() => setShowDeleteConfirm(false)}>取消</Button>
<Button onClick={handleDeleteFactory} color="error" variant="contained">
删除
</Button>
</DialogActions>
</Dialog>
</>
);
};
// 系统默认模型设置组件
interface SystemModelSettingProps {
myLlm: Record<string, IMyLlmModel> | undefined;
}
const SystemModelSetting: React.FC<SystemModelSettingProps> = ({ myLlm }) => {
const [defaultModel, setDefaultModel] = useState('');
const [loading, setLoading] = useState(false);
// 获取所有可用的聊天模型
const chatModels = myLlm ? Object.values(myLlm).flatMap(group =>
group.llm.filter(model => model.type.toLowerCase().includes('chat') || model.type.toLowerCase().includes('llm'))
) : [];
const handleSaveDefaultModel = async () => {
if (!defaultModel) return;
setLoading(true);
try {
// TODO: 调用设置系统默认模型的 API
console.log('Setting default model:', defaultModel);
// await userService.setSystemDefaultModel({ model: defaultModel });
} catch (error) {
console.error('设置默认模型失败:', error);
} finally {
setLoading(false);
}
};
return (
<Card sx={{ mb: 3 }}>
<CardContent>
<Typography variant="h6" gutterBottom>
系统默认 LLM 模型
</Typography>
<Typography variant="body2" color="text.secondary" gutterBottom>
设置系统默认使用的大语言模型,用于对话和文本生成任务。
</Typography>
<Box display="flex" gap={2} alignItems="center" mt={2}>
<FormControl sx={{ minWidth: 300 }}>
<InputLabel>选择默认模型</InputLabel>
<Select
value={defaultModel}
label="选择默认模型"
onChange={(e) => setDefaultModel(e.target.value)}
>
{chatModels.map((model) => (
<MenuItem key={`${model.name}-${model.type}`} value={model.name}>
{model.name} ({model.type})
</MenuItem>
))}
</Select>
</FormControl>
<Button
variant="contained"
onClick={handleSaveDefaultModel}
disabled={!defaultModel || loading}
startIcon={loading ? <CircularProgress size={16} /> : undefined}
>
保存设置
</Button>
</Box>
</CardContent>
</Card>
);
};
// 主页面组件 // 主页面组件
function ModelsPage() { function ModelsPage() {
@@ -336,116 +97,124 @@ function ModelsPage() {
管理您的 LLM 模型工厂和个人模型配置 管理您的 LLM 模型工厂和个人模型配置
</Typography> </Typography>
{/* 系统默认模型设置 */}
<SystemModelSetting myLlm={myLlm} />
{/* My LLM 部分 */} {/* My LLM 部分 */}
{/* <Box mb={4}> <Box mb={4} mt={2}>
<Typography variant="h5" gutterBottom>
我的 LLM 模型
</Typography>
{!myLlm || Object.keys(myLlm).length === 0 ? ( {!myLlm || Object.keys(myLlm).length === 0 ? (
<Alert severity="info"> <Alert severity="info">
您还没有配置任何 LLM 模型。请在下方的模型工厂中进行配置。 您还没有配置任何 LLM 模型。请在下方的模型工厂中进行配置。
</Alert> </Alert>
) : ( ) : (
<Grid container spacing={2}> <Accordion defaultExpanded>
{Object.entries(myLlm).map(([factoryName, group]) => ( <AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Grid size={12} key={factoryName}> <Typography variant="h5" gutterBottom>
<Card variant="outlined"> 我的 LLM 模型
<CardContent> </Typography>
<Typography variant="h6" gutterBottom> </AccordionSummary>
{factoryName} <AccordionDetails>
</Typography> <Grid container spacing={2}>
<Box display="flex" gap={1} mb={2}> {Object.entries(myLlm).map(([factoryName, group]) => (
{group.tags.split(',').map((tag) => ( <Grid size={12} key={factoryName}>
<Chip <Card variant="outlined">
key={tag} <CardContent>
label={tag.trim()} <Typography variant="h6" gutterBottom>
size="small" {factoryName}
sx={{ </Typography>
backgroundColor: MODEL_TYPE_COLORS[tag.trim()] || '#757575', <Box display="flex" gap={1} mb={2}>
color: 'white', {group.tags.split(',').map((tag) => (
}}
/>
))}
</Box>
<Grid container spacing={2}>
{group.llm.map((model) => (
<Grid size={{ xs: 12, sm: 6, md: 4 }} key={model.name}>
<Card variant="outlined" sx={{ p: 2 }}>
<Box display="flex" justifyContent="space-between" alignItems="flex-start" mb={1}>
<Typography variant="body2" fontWeight="bold">
{model.name}
</Typography>
<Box>
<IconButton
size="small"
onClick={() => handleEditModel({ name: factoryName } as IFactory, model)}
>
<EditIcon fontSize="small" />
</IconButton>
<IconButton
size="small"
color="error"
onClick={() => handleDeleteModel(factoryName, model.name)}
>
<DeleteIcon fontSize="small" />
</IconButton>
</Box>
</Box>
<Chip <Chip
label={model.type} key={tag}
label={tag.trim()}
size="small" size="small"
sx={{ sx={{
backgroundColor: MODEL_TYPE_COLORS[model.type.toUpperCase()] || '#757575', backgroundColor: MODEL_TYPE_COLORS[tag.trim()] || '#757575',
color: 'white', color: 'white',
mb: 1,
}} }}
/> />
<Typography variant="caption" display="block" color="text.secondary"> ))}
Max Tokens: {model.max_tokens} </Box>
</Typography> <Grid container spacing={2}>
<Typography variant="caption" display="block" color="text.secondary"> {group.llm.map((model) => (
Used: {model.used_token} <Grid size={{ xs: 12, sm: 6, md: 4 }} key={model.name}>
</Typography> <Card variant="outlined" sx={{ p: 2 }}>
{model.api_base && ( <Box display="flex" justifyContent="space-between" alignItems="flex-start" mb={1}>
<Typography variant="caption" display="block" color="text.secondary"> <Typography variant="body2" fontWeight="bold">
Base URL: {model.api_base} {model.name}
</Typography> </Typography>
)} <Box>
</Card> <IconButton
size="small"
onClick={() => handleEditModel({ name: factoryName } as IFactory, model)}
>
<EditIcon fontSize="small" />
</IconButton>
<IconButton
size="small"
color="error"
onClick={() => handleDeleteModel(factoryName, model.name)}
>
<DeleteIcon fontSize="small" />
</IconButton>
</Box>
</Box>
<Chip
label={model.type}
size="small"
sx={{
backgroundColor: MODEL_TYPE_COLORS[model.type.toUpperCase()] || '#757575',
color: 'white',
mb: 1,
}}
/>
<Typography variant="caption" display="block" color="text.secondary">
Max Tokens: {model.max_tokens}
</Typography>
<Typography variant="caption" display="block" color="text.secondary">
Used: {model.used_token}
</Typography>
{model.api_base && (
<Typography variant="caption" display="block" color="text.secondary">
Base URL: {model.api_base}
</Typography>
)}
</Card>
</Grid>
))}
</Grid> </Grid>
))} </CardContent>
</Grid> </Card>
</CardContent> </Grid>
</Card> ))}
</Grid> </Grid>
))} </AccordionDetails>
</Grid> </Accordion>
)} )}
</Box> */} </Box>
{/* LLM Factory 部分 */} {/* LLM Factory 部分 */}
<Box> <Box>
<Typography variant="h5" gutterBottom> <Accordion defaultExpanded>
LLM 模型工厂 <AccordionSummary expandIcon={<ExpandMoreIcon />}>
</Typography> <Typography variant="h5" gutterBottom>
<Typography variant="body2" color="text.secondary" gutterBottom> LLM 模型工厂
配置各种 AI 服务提供商的模型 </Typography>
</Typography> <AppSvgIcon name='arxiv' />
</AccordionSummary>
{llmFactory.map((factory) => ( <AccordionDetails>
<ModelFactoryCard <Grid container spacing={2}>
key={factory.name} {
factory={factory} llmFactory.map((factory) => (
myModels={getModelsForFactory(factory.name as LLMFactory)} <Grid size={{ xs: 6, sm: 4, md: 3 }} key={factory.name}>
onConfigure={handleConfigureFactory} <LLMFactoryCard
onDeleteFactory={handleDeleteFactory} key={factory.name}
onDeleteModel={handleDeleteModel} factory={factory}
onEditModel={handleEditModel} onConfigure={handleConfigureFactory}
/> />
))} </Grid>
))
}
</Grid>
</AccordionDetails>
</Accordion>
</Box> </Box>
{/* 模型配置对话框 */} {/* 模型配置对话框 */}
+9 -1
View File
@@ -5,7 +5,15 @@ import svgr from "vite-plugin-svgr";
// https://vite.dev/config/ // https://vite.dev/config/
export default defineConfig({ export default defineConfig({
plugins: [react(), svgr()], plugins: [
react(),
svgr({
svgrOptions: {
icon: true,
prettier: true,
}
})
],
resolve: { resolve: {
alias: { alias: {
'@': path.resolve(__dirname, './src'), '@': path.resolve(__dirname, './src'),