fragmenta / app /frontend /src /components /CheckpointManager.js
MazCodes's picture
Upload folder using huggingface_hub
0573fbf verified
raw
history blame
9.19 kB
import React, { useState } from 'react';
import {
Box,
Typography,
Card,
Chip,
Button,
Alert,
Dialog,
DialogTitle,
DialogContent,
DialogActions,
Snackbar,
} from '@mui/material';
import { CloudDownload as CloudDownloadIcon, Trash2 as DeleteIcon } from 'lucide-react';
import api from '../api';
import { checkpointManagerStyles } from '../theme';
export default function CheckpointManager({ model, onRefresh }) {
const [loadingStates, setLoadingStates] = useState({});
const [error, setError] = useState(null);
const [deleteTarget, setDeleteTarget] = useState(null);
const [toast, setToast] = useState({
open: false,
message: '',
severity: 'success'
});
const handleUnwrapCheckpoint = async (checkpoint) => {
const checkpointId = checkpoint.path;
setLoadingStates(prev => ({ ...prev, [checkpointId]: { unwrapping: true } }));
setError(null);
try {
await api.post('/api/unwrap-model', {
model_config: model.config_path,
ckpt_path: checkpoint.path,
name: `${checkpoint.name}_unwrapped`
});
setError(null);
setToast({
open: true,
message: `Checkpoint "${checkpoint.name}" unwrapped successfully.`,
severity: 'success'
});
onRefresh();
} catch (err) {
setError(`Failed to unwrap ${checkpoint.name}: ${err.response?.data?.error || err.message}`);
} finally {
setLoadingStates(prev => ({ ...prev, [checkpointId]: { unwrapping: false } }));
}
};
const handleDeleteCheckpoint = async () => {
if (!deleteTarget) {
return;
}
const checkpointId = deleteTarget.path;
setLoadingStates(prev => ({ ...prev, [checkpointId]: { deleting: true } }));
setError(null);
try {
await api.post('/api/delete-checkpoint', {
checkpoint_path: deleteTarget.path
});
setToast({
open: true,
message: `Checkpoint "${deleteTarget.name}" deleted successfully.`,
severity: 'success'
});
onRefresh();
} catch (err) {
setError(`Failed to delete ${deleteTarget.name}: ${err.response?.data?.error || err.message}`);
} finally {
setDeleteTarget(null);
setLoadingStates(prev => ({ ...prev, [checkpointId]: { deleting: false } }));
}
};
const closeToast = (_, reason) => {
if (reason === 'clickaway') {
return;
}
setToast(prev => ({ ...prev, open: false }));
};
const checkpoints = model.checkpoints || [];
return (
<>
<Box sx={checkpointManagerStyles.root}>
<Typography variant="subtitle2" color="textSecondary">
Checkpoints ({checkpoints.length})
</Typography>
{checkpoints.length === 0 ? (
<Typography variant="caption" color="textSecondary" sx={checkpointManagerStyles.emptyText}>
No checkpoints yet.
</Typography>
) : (
<Box sx={checkpointManagerStyles.checkpointsList}>
{checkpoints.map((checkpoint, index) => {
const checkpointId = checkpoint.path;
const isUnwrapping = loadingStates[checkpointId]?.unwrapping;
const isDeleting = loadingStates[checkpointId]?.deleting;
const hasUnwrappedVersion = model.unwrapped_models?.some(unwrapped =>
unwrapped.name.includes(checkpoint.name) ||
checkpoint.name.includes(unwrapped.name.replace('_unwrapped', ''))
);
return (
<Card key={index} sx={checkpointManagerStyles.checkpointCard}>
<Box sx={checkpointManagerStyles.checkpointRow}>
<Box sx={checkpointManagerStyles.checkpointInfo}>
<Typography variant="body2" sx={checkpointManagerStyles.checkpointName}>
{checkpoint.name}
{hasUnwrappedVersion && (
<Chip
label="Unwrapped"
size="small"
color="success"
sx={checkpointManagerStyles.unwrappedChip}
/>
)}
</Typography>
<Typography variant="caption" color="textSecondary">
{checkpoint.size_mb} MB
</Typography>
</Box>
<Box sx={checkpointManagerStyles.actions}>
{!hasUnwrappedVersion && (
<Button
variant="outlined"
color="primary"
size="small"
startIcon={<CloudDownloadIcon />}
onClick={() => handleUnwrapCheckpoint(checkpoint)}
disabled={isUnwrapping || isDeleting}
>
{isUnwrapping ? 'Unwrapping...' : 'Unwrap'}
</Button>
)}
{hasUnwrappedVersion && (
<Button
variant="outlined"
color="error"
size="small"
startIcon={<DeleteIcon />}
onClick={() => setDeleteTarget(checkpoint)}
disabled={isDeleting || isUnwrapping}
>
{isDeleting ? 'Deleting...' : 'Delete'}
</Button>
)}
</Box>
</Box>
</Card>
);
})}
</Box>
)}
{error && (
<Alert severity="error" sx={checkpointManagerStyles.errorAlert}>{error}</Alert>
)}
</Box>
<Dialog
open={Boolean(deleteTarget)}
onClose={() => setDeleteTarget(null)}
aria-labelledby="delete-checkpoint-dialog-title"
>
<DialogTitle id="delete-checkpoint-dialog-title">
Delete Wrapped Checkpoint
</DialogTitle>
<DialogContent>
<Typography sx={checkpointManagerStyles.deleteDialogText}>
{deleteTarget
? `Delete "${deleteTarget.name}"? This action cannot be undone.`
: 'Delete this checkpoint?'}
</Typography>
</DialogContent>
<DialogActions>
<Button onClick={() => setDeleteTarget(null)}>
Cancel
</Button>
<Button
variant="contained"
color="error"
onClick={handleDeleteCheckpoint}
>
Delete
</Button>
</DialogActions>
</Dialog>
<Snackbar
open={toast.open}
autoHideDuration={3200}
onClose={closeToast}
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
>
<Alert onClose={closeToast} severity={toast.severity} variant="filled" sx={checkpointManagerStyles.snackbarAlert}>
{toast.message}
</Alert>
</Snackbar>
</>
);
}