Files
YG-Datasets/easy-dataset-main/components/export/HuggingFaceTab.js

246 lines
6.7 KiB
JavaScript
Raw Normal View History

2026-03-17 14:36:31 +08:00
// HuggingFaceTab.js 组件
import React, { useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import {
Typography,
Box,
TextField,
Button,
FormControlLabel,
Checkbox,
Alert,
CircularProgress,
Divider,
Paper,
Grid,
Tooltip,
IconButton,
Link
} from '@mui/material';
import InfoIcon from '@mui/icons-material/Info';
import HelpOutlineIcon from '@mui/icons-material/HelpOutline';
import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline';
const HuggingFaceTab = ({
projectId,
systemPrompt,
reasoningLanguage,
confirmedOnly,
includeCOT,
formatType,
fileFormat,
customFields,
handleSystemPromptChange,
handleReasoningLanguageChange,
handleConfirmedOnlyChange,
handleIncludeCOTChange
}) => {
const { t } = useTranslation();
const [token, setToken] = useState('');
const [datasetName, setDatasetName] = useState('');
const [isPrivate, setIsPrivate] = useState(false);
const [uploading, setUploading] = useState(false);
const [error, setError] = useState('');
const [success, setSuccess] = useState(false);
const [datasetUrl, setDatasetUrl] = useState('');
const [hasToken, setHasToken] = useState(false);
const [loading, setLoading] = useState(true);
// 从配置中获取 huggingfaceToken
useEffect(() => {
if (projectId) {
setLoading(true);
fetch(`/api/projects/${projectId}/config`)
.then(res => res.json())
.then(data => {
if (data.huggingfaceToken) {
setToken(data.huggingfaceToken);
setHasToken(true);
}
setLoading(false);
})
.catch(err => {
console.error('获取 HuggingFace Token 失败:', err);
setLoading(false);
});
}
}, [projectId]);
// 处理上传数据集到 HuggingFace
const handleUpload = async () => {
if (!hasToken) {
return;
}
if (!datasetName) {
setError('请输入数据集名称');
return;
}
try {
setUploading(true);
setError('');
setSuccess(false);
const response = await fetch(`/api/projects/${projectId}/huggingface/upload`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
token,
datasetName,
isPrivate,
formatType,
systemPrompt,
reasoningLanguage,
confirmedOnly,
includeCOT,
fileFormat,
customFields: formatType === 'custom' ? customFields : undefined
})
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error || '上传失败');
}
setSuccess(true);
setDatasetUrl(data.url);
} catch (err) {
setError(err.message);
} finally {
setUploading(false);
}
};
return (
<Box sx={{ mt: 2 }}>
{error && (
<Alert severity="error" sx={{ mb: 2 }}>
{error}
</Alert>
)}
{success && (
<Alert severity="success" sx={{ mb: 2 }} icon={<CheckCircleOutlineIcon fontSize="inherit" />}>
{t('export.uploadSuccess')}
{datasetUrl && (
<Box mt={1}>
<Link href={datasetUrl} target="_blank" rel="noopener noreferrer">
{t('export.viewOnHuggingFace')}
</Link>
</Box>
)}
</Alert>
)}
{!hasToken ? (
<Alert severity="warning" sx={{ mb: 3 }}>
{t('export.noTokenWarning')}
<Box mt={1}>
<Button
variant="outlined"
size="small"
onClick={() => (window.location.href = `/projects/${projectId}/settings`)}
>
{t('export.goToSettings')}
</Button>
</Box>
</Alert>
) : null}
<Divider sx={{ my: 2 }} />
<Box sx={{ mb: 3 }}>
<Typography variant="subtitle1" fontWeight="bold" gutterBottom>
{t('export.datasetSettings')}
</Typography>
<Grid container spacing={2}>
<Grid item xs={12}>
<TextField
fullWidth
label={t('export.datasetName')}
placeholder="username/dataset-name"
value={datasetName}
onChange={e => setDatasetName(e.target.value)}
helperText={t('export.datasetNameHelp')}
sx={{ mb: 2 }}
/>
</Grid>
<Grid item xs={12}>
<FormControlLabel
control={<Checkbox checked={isPrivate} onChange={e => setIsPrivate(e.target.checked)} />}
label={t('export.privateDataset')}
/>
</Grid>
</Grid>
</Box>
<Box sx={{ mb: 3 }}>
<Typography variant="subtitle1" fontWeight="bold" gutterBottom>
{t('export.exportOptions')}
</Typography>
<Box sx={{ mb: 2 }}>
<Typography variant="subtitle2" gutterBottom>
{t('export.systemPrompt')}
</Typography>
<TextField
fullWidth
multiline
rows={3}
value={systemPrompt}
onChange={handleSystemPromptChange}
variant="outlined"
/>
</Box>
{/* Reasoning language only for multilingualthinking */}
{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={{ 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>
</Box>
<Box sx={{ display: 'flex', justifyContent: 'flex-end', mt: 3 }}>
<Button
variant="contained"
onClick={handleUpload}
disabled={uploading || !hasToken || !datasetName}
sx={{ borderRadius: 2 }}
>
{uploading ? <CircularProgress size={24} /> : t('export.uploadToHuggingFace')}
</Button>
</Box>
</Box>
);
};
export default HuggingFaceTab;