Files
TERES_web_frontend/src/pages/PipelineConfig.tsx

349 lines
11 KiB
TypeScript
Raw Normal View History

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;