Files
TERES_web_frontend/src/pages/PipelineConfig.tsx

349 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 size={{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 size={{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 size={{xs:12,md:6}}>
<ConfigCard>
<CardContent>
<Typography variant="h6" fontWeight={600} mb={2}>
Pipeline
</Typography>
<Grid container spacing={2}>
<Grid size={{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 size={{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 size={{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 size={{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;