349 lines
11 KiB
TypeScript
349 lines
11 KiB
TypeScript
|
|
import React, { useState } from 'react';
|
|||
|
|
import {
|
|||
|
|
Box,
|
|||
|
|
Typography,
|
|||
|
|
Card,
|
|||
|
|
CardContent,
|
|||
|
|
Grid,
|
|||
|
|
Button,
|
|||
|
|
Switch,
|
|||
|
|
FormControlLabel,
|
|||
|
|
Slider,
|
|||
|
|
TextField,
|
|||
|
|
Select,
|
|||
|
|
MenuItem,
|
|||
|
|
FormControl,
|
|||
|
|
InputLabel,
|
|||
|
|
Chip,
|
|||
|
|
Divider,
|
|||
|
|
Alert,
|
|||
|
|
LinearProgress,
|
|||
|
|
} from '@mui/material';
|
|||
|
|
import {
|
|||
|
|
Settings as SettingsIcon,
|
|||
|
|
PlayArrow as PlayIcon,
|
|||
|
|
Stop as StopIcon,
|
|||
|
|
Refresh as RefreshIcon,
|
|||
|
|
Save as SaveIcon,
|
|||
|
|
} from '@mui/icons-material';
|
|||
|
|
import { styled } from '@mui/material/styles';
|
|||
|
|
|
|||
|
|
const PageContainer = styled(Box)(({ theme }) => ({
|
|||
|
|
padding: '1.5rem',
|
|||
|
|
backgroundColor: '#F8F9FA',
|
|||
|
|
minHeight: 'calc(100vh - 60px)',
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
const PageHeader = styled(Box)(({ theme }) => ({
|
|||
|
|
display: 'flex',
|
|||
|
|
justifyContent: 'space-between',
|
|||
|
|
alignItems: 'center',
|
|||
|
|
marginBottom: '1.5rem',
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
const ConfigCard = styled(Card)(({ theme }) => ({
|
|||
|
|
marginBottom: '1.5rem',
|
|||
|
|
border: '1px solid #E5E5E5',
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
const ConfigSection = styled(Box)(({ theme }) => ({
|
|||
|
|
marginBottom: '1.5rem',
|
|||
|
|
'&:last-child': {
|
|||
|
|
marginBottom: 0,
|
|||
|
|
},
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
const StatusIndicator = styled(Box)<{ status: 'running' | 'stopped' | 'error' }>(({ status, theme }) => ({
|
|||
|
|
display: 'inline-flex',
|
|||
|
|
alignItems: 'center',
|
|||
|
|
gap: '0.5rem',
|
|||
|
|
padding: '0.25rem 0.75rem',
|
|||
|
|
borderRadius: '12px',
|
|||
|
|
fontSize: '0.875rem',
|
|||
|
|
fontWeight: 500,
|
|||
|
|
backgroundColor:
|
|||
|
|
status === 'running' ? '#E8F5E8' :
|
|||
|
|
status === 'stopped' ? '#F8F9FA' : '#F8D7DA',
|
|||
|
|
color:
|
|||
|
|
status === 'running' ? '#155724' :
|
|||
|
|
status === 'stopped' ? '#666' : '#721C24',
|
|||
|
|
'&::before': {
|
|||
|
|
content: '""',
|
|||
|
|
width: '8px',
|
|||
|
|
height: '8px',
|
|||
|
|
borderRadius: '50%',
|
|||
|
|
backgroundColor:
|
|||
|
|
status === 'running' ? '#28A745' :
|
|||
|
|
status === 'stopped' ? '#6C757D' : '#DC3545',
|
|||
|
|
},
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
const PipelineConfig: React.FC = () => {
|
|||
|
|
const [pipelineStatus, setPipelineStatus] = useState<'running' | 'stopped' | 'error'>('stopped');
|
|||
|
|
const [config, setConfig] = useState({
|
|||
|
|
enabled: false,
|
|||
|
|
chunkSize: 512,
|
|||
|
|
chunkOverlap: 50,
|
|||
|
|
embeddingModel: 'text-embedding-ada-002',
|
|||
|
|
retrievalTopK: 5,
|
|||
|
|
temperature: 0.7,
|
|||
|
|
maxTokens: 2048,
|
|||
|
|
systemPrompt: '你是一个专业的AI助手,请基于提供的知识库内容回答用户问题。',
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const handleConfigChange = (key: string, value: any) => {
|
|||
|
|
setConfig(prev => ({ ...prev, [key]: value }));
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const handleStartPipeline = () => {
|
|||
|
|
setPipelineStatus('running');
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const handleStopPipeline = () => {
|
|||
|
|
setPipelineStatus('stopped');
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const handleSaveConfig = () => {
|
|||
|
|
// 保存配置逻辑
|
|||
|
|
console.log('保存配置:', config);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<PageContainer>
|
|||
|
|
<PageHeader>
|
|||
|
|
<Box>
|
|||
|
|
<Typography variant="h4" fontWeight={600} color="#333">
|
|||
|
|
RAG Pipeline 配置
|
|||
|
|
</Typography>
|
|||
|
|
<Box display="flex" alignItems="center" gap={2} mt={1}>
|
|||
|
|
<StatusIndicator status={pipelineStatus}>
|
|||
|
|
{pipelineStatus === 'running' ? '运行中' :
|
|||
|
|
pipelineStatus === 'stopped' ? '已停止' : '错误'}
|
|||
|
|
</StatusIndicator>
|
|||
|
|
{pipelineStatus === 'running' && (
|
|||
|
|
<LinearProgress sx={{ width: '200px', height: '6px', borderRadius: '3px' }} />
|
|||
|
|
)}
|
|||
|
|
</Box>
|
|||
|
|
</Box>
|
|||
|
|
<Box display="flex" gap={1}>
|
|||
|
|
<Button
|
|||
|
|
variant="outlined"
|
|||
|
|
startIcon={<SaveIcon />}
|
|||
|
|
onClick={handleSaveConfig}
|
|||
|
|
>
|
|||
|
|
保存配置
|
|||
|
|
</Button>
|
|||
|
|
{pipelineStatus === 'running' ? (
|
|||
|
|
<Button
|
|||
|
|
variant="contained"
|
|||
|
|
color="error"
|
|||
|
|
startIcon={<StopIcon />}
|
|||
|
|
onClick={handleStopPipeline}
|
|||
|
|
>
|
|||
|
|
停止
|
|||
|
|
</Button>
|
|||
|
|
) : (
|
|||
|
|
<Button
|
|||
|
|
variant="contained"
|
|||
|
|
startIcon={<PlayIcon />}
|
|||
|
|
onClick={handleStartPipeline}
|
|||
|
|
>
|
|||
|
|
启动
|
|||
|
|
</Button>
|
|||
|
|
)}
|
|||
|
|
</Box>
|
|||
|
|
</PageHeader>
|
|||
|
|
|
|||
|
|
<Grid container spacing={3}>
|
|||
|
|
<Grid item xs={12} md={6}>
|
|||
|
|
<ConfigCard>
|
|||
|
|
<CardContent>
|
|||
|
|
<Typography variant="h6" fontWeight={600} mb={2}>
|
|||
|
|
<SettingsIcon sx={{ mr: 1, verticalAlign: 'middle' }} />
|
|||
|
|
基础配置
|
|||
|
|
</Typography>
|
|||
|
|
|
|||
|
|
<ConfigSection>
|
|||
|
|
<FormControlLabel
|
|||
|
|
control={
|
|||
|
|
<Switch
|
|||
|
|
checked={config.enabled}
|
|||
|
|
onChange={(e) => handleConfigChange('enabled', e.target.checked)}
|
|||
|
|
/>
|
|||
|
|
}
|
|||
|
|
label="启用 RAG Pipeline"
|
|||
|
|
/>
|
|||
|
|
</ConfigSection>
|
|||
|
|
|
|||
|
|
<ConfigSection>
|
|||
|
|
<Typography gutterBottom>文档分块大小</Typography>
|
|||
|
|
<Slider
|
|||
|
|
value={config.chunkSize}
|
|||
|
|
onChange={(_, value) => handleConfigChange('chunkSize', value)}
|
|||
|
|
min={128}
|
|||
|
|
max={2048}
|
|||
|
|
step={64}
|
|||
|
|
marks={[
|
|||
|
|
{ value: 128, label: '128' },
|
|||
|
|
{ value: 512, label: '512' },
|
|||
|
|
{ value: 1024, label: '1024' },
|
|||
|
|
{ value: 2048, label: '2048' },
|
|||
|
|
]}
|
|||
|
|
valueLabelDisplay="on"
|
|||
|
|
/>
|
|||
|
|
</ConfigSection>
|
|||
|
|
|
|||
|
|
<ConfigSection>
|
|||
|
|
<Typography gutterBottom>分块重叠 (%)</Typography>
|
|||
|
|
<Slider
|
|||
|
|
value={config.chunkOverlap}
|
|||
|
|
onChange={(_, value) => handleConfigChange('chunkOverlap', value)}
|
|||
|
|
min={0}
|
|||
|
|
max={50}
|
|||
|
|
step={5}
|
|||
|
|
valueLabelDisplay="on"
|
|||
|
|
/>
|
|||
|
|
</ConfigSection>
|
|||
|
|
|
|||
|
|
<ConfigSection>
|
|||
|
|
<FormControl fullWidth>
|
|||
|
|
<InputLabel>嵌入模型</InputLabel>
|
|||
|
|
<Select
|
|||
|
|
value={config.embeddingModel}
|
|||
|
|
onChange={(e) => handleConfigChange('embeddingModel', e.target.value)}
|
|||
|
|
label="嵌入模型"
|
|||
|
|
>
|
|||
|
|
<MenuItem value="text-embedding-ada-002">text-embedding-ada-002</MenuItem>
|
|||
|
|
<MenuItem value="text-embedding-3-small">text-embedding-3-small</MenuItem>
|
|||
|
|
<MenuItem value="text-embedding-3-large">text-embedding-3-large</MenuItem>
|
|||
|
|
</Select>
|
|||
|
|
</FormControl>
|
|||
|
|
</ConfigSection>
|
|||
|
|
</CardContent>
|
|||
|
|
</ConfigCard>
|
|||
|
|
</Grid>
|
|||
|
|
|
|||
|
|
<Grid item xs={12} md={6}>
|
|||
|
|
<ConfigCard>
|
|||
|
|
<CardContent>
|
|||
|
|
<Typography variant="h6" fontWeight={600} mb={2}>
|
|||
|
|
检索与生成配置
|
|||
|
|
</Typography>
|
|||
|
|
|
|||
|
|
<ConfigSection>
|
|||
|
|
<TextField
|
|||
|
|
fullWidth
|
|||
|
|
label="检索文档数量 (Top-K)"
|
|||
|
|
type="number"
|
|||
|
|
value={config.retrievalTopK}
|
|||
|
|
onChange={(e) => handleConfigChange('retrievalTopK', parseInt(e.target.value))}
|
|||
|
|
inputProps={{ min: 1, max: 20 }}
|
|||
|
|
/>
|
|||
|
|
</ConfigSection>
|
|||
|
|
|
|||
|
|
<ConfigSection>
|
|||
|
|
<Typography gutterBottom>生成温度</Typography>
|
|||
|
|
<Slider
|
|||
|
|
value={config.temperature}
|
|||
|
|
onChange={(_, value) => handleConfigChange('temperature', value)}
|
|||
|
|
min={0}
|
|||
|
|
max={1}
|
|||
|
|
step={0.1}
|
|||
|
|
marks={[
|
|||
|
|
{ value: 0, label: '0' },
|
|||
|
|
{ value: 0.5, label: '0.5' },
|
|||
|
|
{ value: 1, label: '1' },
|
|||
|
|
]}
|
|||
|
|
valueLabelDisplay="on"
|
|||
|
|
/>
|
|||
|
|
</ConfigSection>
|
|||
|
|
|
|||
|
|
<ConfigSection>
|
|||
|
|
<TextField
|
|||
|
|
fullWidth
|
|||
|
|
label="最大输出Token数"
|
|||
|
|
type="number"
|
|||
|
|
value={config.maxTokens}
|
|||
|
|
onChange={(e) => handleConfigChange('maxTokens', parseInt(e.target.value))}
|
|||
|
|
inputProps={{ min: 256, max: 4096 }}
|
|||
|
|
/>
|
|||
|
|
</ConfigSection>
|
|||
|
|
|
|||
|
|
<ConfigSection>
|
|||
|
|
<TextField
|
|||
|
|
fullWidth
|
|||
|
|
label="系统提示词"
|
|||
|
|
multiline
|
|||
|
|
rows={4}
|
|||
|
|
value={config.systemPrompt}
|
|||
|
|
onChange={(e) => handleConfigChange('systemPrompt', e.target.value)}
|
|||
|
|
/>
|
|||
|
|
</ConfigSection>
|
|||
|
|
</CardContent>
|
|||
|
|
</ConfigCard>
|
|||
|
|
</Grid>
|
|||
|
|
|
|||
|
|
<Grid item xs={12}>
|
|||
|
|
<ConfigCard>
|
|||
|
|
<CardContent>
|
|||
|
|
<Typography variant="h6" fontWeight={600} mb={2}>
|
|||
|
|
Pipeline 状态监控
|
|||
|
|
</Typography>
|
|||
|
|
|
|||
|
|
<Grid container spacing={2}>
|
|||
|
|
<Grid item xs={12} sm={6} md={3}>
|
|||
|
|
<Box textAlign="center" p={2} bgcolor="#F8F9FA" borderRadius="6px">
|
|||
|
|
<Typography variant="h4" color="primary" fontWeight={600}>
|
|||
|
|
1,234
|
|||
|
|
</Typography>
|
|||
|
|
<Typography variant="body2" color="text.secondary">
|
|||
|
|
已处理文档
|
|||
|
|
</Typography>
|
|||
|
|
</Box>
|
|||
|
|
</Grid>
|
|||
|
|
<Grid item xs={12} sm={6} md={3}>
|
|||
|
|
<Box textAlign="center" p={2} bgcolor="#F8F9FA" borderRadius="6px">
|
|||
|
|
<Typography variant="h4" color="success.main" fontWeight={600}>
|
|||
|
|
98.5%
|
|||
|
|
</Typography>
|
|||
|
|
<Typography variant="body2" color="text.secondary">
|
|||
|
|
成功率
|
|||
|
|
</Typography>
|
|||
|
|
</Box>
|
|||
|
|
</Grid>
|
|||
|
|
<Grid item xs={12} sm={6} md={3}>
|
|||
|
|
<Box textAlign="center" p={2} bgcolor="#F8F9FA" borderRadius="6px">
|
|||
|
|
<Typography variant="h4" color="warning.main" fontWeight={600}>
|
|||
|
|
2.3s
|
|||
|
|
</Typography>
|
|||
|
|
<Typography variant="body2" color="text.secondary">
|
|||
|
|
平均响应时间
|
|||
|
|
</Typography>
|
|||
|
|
</Box>
|
|||
|
|
</Grid>
|
|||
|
|
<Grid item xs={12} sm={6} md={3}>
|
|||
|
|
<Box textAlign="center" p={2} bgcolor="#F8F9FA" borderRadius="6px">
|
|||
|
|
<Typography variant="h4" color="info.main" fontWeight={600}>
|
|||
|
|
156
|
|||
|
|
</Typography>
|
|||
|
|
<Typography variant="body2" color="text.secondary">
|
|||
|
|
今日查询数
|
|||
|
|
</Typography>
|
|||
|
|
</Box>
|
|||
|
|
</Grid>
|
|||
|
|
</Grid>
|
|||
|
|
|
|||
|
|
{pipelineStatus === 'error' && (
|
|||
|
|
<Alert severity="error" sx={{ mt: 2 }}>
|
|||
|
|
Pipeline 运行出现错误,请检查配置和日志。
|
|||
|
|
</Alert>
|
|||
|
|
)}
|
|||
|
|
</CardContent>
|
|||
|
|
</ConfigCard>
|
|||
|
|
</Grid>
|
|||
|
|
</Grid>
|
|||
|
|
</PageContainer>
|
|||
|
|
);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
export default PipelineConfig;
|