185 lines
5.2 KiB
JavaScript
185 lines
5.2 KiB
JavaScript
|
|
// LlamaFactoryTab.js 组件
|
|||
|
|
import React, { useState, useEffect } from 'react';
|
|||
|
|
import { useTranslation } from 'react-i18next';
|
|||
|
|
import {
|
|||
|
|
Button,
|
|||
|
|
FormControlLabel,
|
|||
|
|
Checkbox,
|
|||
|
|
Typography,
|
|||
|
|
Box,
|
|||
|
|
TextField,
|
|||
|
|
Alert,
|
|||
|
|
CircularProgress,
|
|||
|
|
IconButton,
|
|||
|
|
Tooltip
|
|||
|
|
} from '@mui/material';
|
|||
|
|
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
|
|||
|
|
import CheckIcon from '@mui/icons-material/Check';
|
|||
|
|
|
|||
|
|
const LlamaFactoryTab = ({
|
|||
|
|
projectId,
|
|||
|
|
systemPrompt,
|
|||
|
|
reasoningLanguage,
|
|||
|
|
confirmedOnly,
|
|||
|
|
includeCOT,
|
|||
|
|
formatType,
|
|||
|
|
handleSystemPromptChange,
|
|||
|
|
handleReasoningLanguageChange,
|
|||
|
|
handleConfirmedOnlyChange,
|
|||
|
|
handleIncludeCOTChange
|
|||
|
|
}) => {
|
|||
|
|
const { t } = useTranslation();
|
|||
|
|
const [configExists, setConfigExists] = useState(false);
|
|||
|
|
const [configPath, setConfigPath] = useState('');
|
|||
|
|
const [generating, setGenerating] = useState(false);
|
|||
|
|
const [error, setError] = useState('');
|
|||
|
|
const [copied, setCopied] = useState(false);
|
|||
|
|
|
|||
|
|
// 检查配置文件是否存在
|
|||
|
|
useEffect(() => {
|
|||
|
|
if (projectId) {
|
|||
|
|
fetch(`/api/projects/${projectId}/llamaFactory/checkConfig`)
|
|||
|
|
.then(res => res.json())
|
|||
|
|
.then(data => {
|
|||
|
|
setConfigExists(data.exists);
|
|||
|
|
if (data.exists) {
|
|||
|
|
setConfigPath(data.configPath);
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
.catch(err => {
|
|||
|
|
setError(err.message);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}, [projectId, configExists]);
|
|||
|
|
|
|||
|
|
// 复制路径到剪贴板
|
|||
|
|
const handleCopyPath = () => {
|
|||
|
|
const path = configPath.replace('dataset_info.json', '');
|
|||
|
|
navigator.clipboard.writeText(path).then(() => {
|
|||
|
|
setCopied(true);
|
|||
|
|
setTimeout(() => setCopied(false), 2000);
|
|||
|
|
});
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 处理生成 Llama Factory 配置
|
|||
|
|
const handleGenerateConfig = async () => {
|
|||
|
|
try {
|
|||
|
|
setGenerating(true);
|
|||
|
|
setError('');
|
|||
|
|
|
|||
|
|
const response = await fetch(`/api/projects/${projectId}/llamaFactory/generate`, {
|
|||
|
|
method: 'POST',
|
|||
|
|
headers: {
|
|||
|
|
'Content-Type': 'application/json'
|
|||
|
|
},
|
|||
|
|
body: JSON.stringify({
|
|||
|
|
formatType,
|
|||
|
|
systemPrompt,
|
|||
|
|
reasoningLanguage,
|
|||
|
|
confirmedOnly,
|
|||
|
|
includeCOT
|
|||
|
|
})
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const data = await response.json();
|
|||
|
|
if (!response.ok) {
|
|||
|
|
throw new Error(data.error);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
setConfigExists(true);
|
|||
|
|
} catch (err) {
|
|||
|
|
setError(err.message);
|
|||
|
|
} finally {
|
|||
|
|
setGenerating(false);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<Box sx={{ mt: 2 }}>
|
|||
|
|
{error && (
|
|||
|
|
<Alert severity="error" sx={{ mt: 2 }}>
|
|||
|
|
{error}
|
|||
|
|
</Alert>
|
|||
|
|
)}
|
|||
|
|
|
|||
|
|
<Box sx={{ mb: 3 }}>
|
|||
|
|
<Typography variant="subtitle1" gutterBottom>
|
|||
|
|
{t('export.systemPrompt')}
|
|||
|
|
</Typography>
|
|||
|
|
<TextField
|
|||
|
|
fullWidth
|
|||
|
|
multiline
|
|||
|
|
rows={3}
|
|||
|
|
value={systemPrompt}
|
|||
|
|
onChange={handleSystemPromptChange}
|
|||
|
|
variant="outlined"
|
|||
|
|
/>
|
|||
|
|
</Box>
|
|||
|
|
{/* Reasoning language – only for multilingual‑thinking */}
|
|||
|
|
{formatType === 'multilingualthinking' && (
|
|||
|
|
<Box sx={{ mb: 3 }}>
|
|||
|
|
<Typography variant="subtitle1" fontWeight="bold" gutterBottom>
|
|||
|
|
{t('export.reasoningLanguage')}
|
|||
|
|
</Typography>
|
|||
|
|
<TextField
|
|||
|
|
fullWidth
|
|||
|
|
rows={3}
|
|||
|
|
multiline
|
|||
|
|
variant="outlined"
|
|||
|
|
placeholder={t('export.reasoningLanguage')}
|
|||
|
|
value={reasoningLanguage}
|
|||
|
|
onChange={handleReasoningLanguageChange}
|
|||
|
|
/>
|
|||
|
|
</Box>
|
|||
|
|
)}
|
|||
|
|
<Box sx={{ mb: 2, display: 'flex', flexDirection: 'row', gap: 4 }}>
|
|||
|
|
<FormControlLabel
|
|||
|
|
control={<Checkbox checked={confirmedOnly} onChange={handleConfirmedOnlyChange} />}
|
|||
|
|
label={t('export.onlyConfirmed')}
|
|||
|
|
/>
|
|||
|
|
|
|||
|
|
<FormControlLabel
|
|||
|
|
control={<Checkbox checked={includeCOT} onChange={handleIncludeCOTChange} />}
|
|||
|
|
label={t('export.includeCOT')}
|
|||
|
|
/>
|
|||
|
|
</Box>
|
|||
|
|
|
|||
|
|
{configExists ? (
|
|||
|
|
<>
|
|||
|
|
<Alert severity="success" sx={{ mb: 2 }}>
|
|||
|
|
{t('export.configExists')}
|
|||
|
|
</Alert>
|
|||
|
|
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
|
|||
|
|
<Typography variant="body2" color="text.secondary">
|
|||
|
|
{t('export.configPath')}: {configPath.replace('dataset_info.json', '')}
|
|||
|
|
</Typography>
|
|||
|
|
<Tooltip title={copied ? t('common.copied') : t('common.copy')}>
|
|||
|
|
<IconButton size="small" onClick={handleCopyPath} sx={{ ml: 1 }}>
|
|||
|
|
{copied ? <CheckIcon fontSize="small" color="success" /> : <ContentCopyIcon fontSize="small" />}
|
|||
|
|
</IconButton>
|
|||
|
|
</Tooltip>
|
|||
|
|
</Box>
|
|||
|
|
</>
|
|||
|
|
) : (
|
|||
|
|
<Typography variant="body2" color="text.secondary" gutterBottom>
|
|||
|
|
{t('export.noConfig')}
|
|||
|
|
</Typography>
|
|||
|
|
)}
|
|||
|
|
|
|||
|
|
<Box sx={{ display: 'flex', justifyContent: 'flex-end', mt: 2 }}>
|
|||
|
|
<Button onClick={handleGenerateConfig} variant="contained" disabled={generating} sx={{ borderRadius: 2 }}>
|
|||
|
|
{generating ? (
|
|||
|
|
<CircularProgress size={24} />
|
|||
|
|
) : configExists ? (
|
|||
|
|
t('export.updateConfig')
|
|||
|
|
) : (
|
|||
|
|
t('export.generateConfig')
|
|||
|
|
)}
|
|||
|
|
</Button>
|
|||
|
|
</Box>
|
|||
|
|
</Box>
|
|||
|
|
);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
export default LlamaFactoryTab;
|