Spaces:
Paused
Paused
File size: 3,111 Bytes
729628a fb23187 729628a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import type { FunnelResponse, FunnelStage } from '../../types/labeleval';
import { SectionCard, fmtInt, fmtPct } from './shared';
function StageBar({ stage, max, color }: { stage: FunnelStage; max: number; color: string }) {
const width = max > 0 ? Math.max(2, (stage.count / max) * 100) : 2;
return (
<Box sx={{ mb: 0.75 }}>
<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 0.25 }}>
<Typography variant="caption" sx={{ color: '#aab4be', textTransform: 'uppercase', letterSpacing: 0.4 }}>
{stage.stage.replace(/_/g, ' ')}
</Typography>
<Typography variant="caption" sx={{ fontFamily: 'monospace' }}>
{fmtInt(stage.count)} · {fmtPct(stage.pct_of_input)}
</Typography>
</Box>
<Box sx={{ height: 16, bgcolor: '#232a31', borderRadius: 1, overflow: 'hidden' }}>
<Box
sx={{
height: '100%',
width: `${width}%`,
background: `linear-gradient(90deg, ${color}, ${color}88)`,
borderRadius: 1,
transition: 'width 0.4s ease',
}}
/>
</Box>
</Box>
);
}
export default function VerificationFunnel({ funnel }: { funnel: FunnelResponse | null }) {
if (!funnel || (funnel.main_path.length === 0 && funnel.side_path.length === 0)) {
return (
<SectionCard title="Verification Funnel">
<Typography variant="body2" sx={{ color: '#8a949e' }}>
No funnel data yet — run the evaluation pipeline to populate it.
</Typography>
</SectionCard>
);
}
const maxMain = Math.max(1, ...funnel.main_path.map((s) => s.count));
const maxSide = Math.max(1, ...funnel.side_path.map((s) => s.count));
return (
<SectionCard
title="Verification Funnel"
help="Where labels are in their lifecycle. Main path: frames → auto-labeled → evaluated → auto-graded → verified. Side branch: labels that failed a quality gate detour through human review, re-labeling and re-validation before re-joining as verified. Percentages are relative to the funnel's input stage."
>
<Box sx={{ display: 'flex', gap: 3, flexWrap: 'wrap' }}>
<Box sx={{ flex: '2 1 380px' }}>
<Typography variant="caption" sx={{ color: '#4fc3f7', fontWeight: 700 }}>
MAIN PATH
</Typography>
<Box sx={{ mt: 1 }}>
{funnel.main_path.map((s) => (
<StageBar key={s.stage} stage={s} max={maxMain} color="#4fc3f7" />
))}
</Box>
</Box>
<Box sx={{ flex: '1 1 300px' }}>
<Typography variant="caption" sx={{ color: '#ffa726', fontWeight: 700 }}>
SIDE BRANCH — FLAGGED → HITL → RE-LABEL → RE-VALIDATION → VERIFIED
</Typography>
<Box sx={{ mt: 1 }}>
{funnel.side_path.map((s) => (
<StageBar key={s.stage} stage={s} max={maxSide} color="#ffa726" />
))}
</Box>
</Box>
</Box>
</SectionCard>
);
}
|