Spaces:
Paused
Paused
Download src/pages/hillclimb/DesignLabView.tsx from whassan/SensorflowStudio: direct link, hf CLI and curl.
- Browser
- Download file 11.2 kB
-
https://huggingface.co/spaces/whassan/SensorflowStudio/resolve/main/src/pages/hillclimb/DesignLabView.tsx
- Command line
-
hf download hf://spaces/whassan/SensorflowStudio/src/pages/hillclimb/DesignLabView.tsx
-
curl -L -o DesignLabView.tsx https://huggingface.co/spaces/whassan/SensorflowStudio/resolve/main/src/pages/hillclimb/DesignLabView.tsx
11.2 kB
| /** Design Lab: challenge brief + pragmatic add-node/connect builder + rationale fields -> graded results. */ | |
| import { useMemo, useState } from 'react'; | |
| import Alert from '@mui/material/Alert'; | |
| import Box from '@mui/material/Box'; | |
| import Button from '@mui/material/Button'; | |
| import Chip from '@mui/material/Chip'; | |
| import IconButton from '@mui/material/IconButton'; | |
| import MenuItem from '@mui/material/MenuItem'; | |
| import TextField from '@mui/material/TextField'; | |
| import Typography from '@mui/material/Typography'; | |
| import { Plus, Trash2 } from 'lucide-react'; | |
| import { ErrorNote, HBar, LoadingBox, SectionCard } from '../../components/labeleval/shared'; | |
| import { PassChip, ScoreChip, useAsync } from '../../components/hillclimb/shared'; | |
| import { getDesignChallenges, submitDesign } from '../../services/hillclimb'; | |
| import type { DesignComponent, DesignEdge, DesignGrade } from '../../types/hillclimb'; | |
| const TYPE_COLORS: Record<string, string> = { | |
| source: '#8d6e63', ingestion: '#4fc3f7', stream: '#29b6f6', batch: '#0288d1', | |
| storage: '#9575cd', feature: '#7e57c2', training: '#ffb74d', inference: '#ffa726', | |
| eval: '#66bb6a', monitoring: '#26a69a', feedback: '#ec407a', | |
| }; | |
| export default function DesignLabView() { | |
| const catalog = useAsync(getDesignChallenges); | |
| const [challengeId, setChallengeId] = useState('petabyte_ingestion'); | |
| const [components, setComponents] = useState<DesignComponent[]>([]); | |
| const [edges, setEdges] = useState<DesignEdge[]>([]); | |
| const [rationales, setRationales] = useState<Record<string, string>>({}); | |
| const [newType, setNewType] = useState('source'); | |
| const [newName, setNewName] = useState(''); | |
| const [newNote, setNewNote] = useState(''); | |
| const [edgeFrom, setEdgeFrom] = useState(''); | |
| const [edgeTo, setEdgeTo] = useState(''); | |
| const [grade, setGrade] = useState<DesignGrade | null>(null); | |
| const [busy, setBusy] = useState(false); | |
| const [error, setError] = useState<string | null>(null); | |
| const challenge = useMemo( | |
| () => catalog.data?.challenges.find((c) => c.challenge_id === challengeId) ?? null, | |
| [catalog.data, challengeId] | |
| ); | |
| const reset = (cid: string) => { | |
| setChallengeId(cid); | |
| setComponents([]); | |
| setEdges([]); | |
| setRationales({}); | |
| setGrade(null); | |
| }; | |
| const addComponent = () => { | |
| const id = `c${components.length + 1}`; | |
| setComponents([...components, { id, type: newType, name: newName || `${newType}-${id}`, note: newNote }]); | |
| setNewName(''); | |
| setNewNote(''); | |
| }; | |
| const removeComponent = (id: string) => { | |
| setComponents(components.filter((c) => c.id !== id)); | |
| setEdges(edges.filter((e) => e.source !== id && e.target !== id)); | |
| }; | |
| const addEdge = () => { | |
| if (!edgeFrom || !edgeTo || edgeFrom === edgeTo) return; | |
| if (edges.some((e) => e.source === edgeFrom && e.target === edgeTo)) return; | |
| setEdges([...edges, { source: edgeFrom, target: edgeTo }]); | |
| }; | |
| const submit = () => { | |
| setBusy(true); | |
| setError(null); | |
| submitDesign(challengeId, components, edges, rationales) | |
| .then(setGrade) | |
| .catch((e: Error) => setError(e.message)) | |
| .finally(() => setBusy(false)); | |
| }; | |
| if (catalog.loading && !catalog.data) return <LoadingBox label="Loading challenges…" />; | |
| if (catalog.error && !catalog.data) return <ErrorNote error={catalog.error} />; | |
| const nameOf = (id: string) => components.find((c) => c.id === id)?.name ?? id; | |
| return ( | |
| <Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}> | |
| <SectionCard | |
| title="Challenge" | |
| action={ | |
| <TextField select size="small" value={challengeId} onChange={(e) => reset(e.target.value)} sx={{ minWidth: 280 }}> | |
| {(catalog.data?.challenges ?? []).map((c) => ( | |
| <MenuItem key={c.challenge_id} value={c.challenge_id}> | |
| {c.title} | |
| </MenuItem> | |
| ))} | |
| </TextField> | |
| } | |
| help="Submit a typed component graph plus a written rationale per key decision. The rule-based grader checks required stages, orphans, feedback-loop closure, single points of failure, capacity math, and rationale coverage of scalability / reliability / latency / cost / observability / failure handling / tradeoffs." | |
| > | |
| {challenge ? ( | |
| <> | |
| <Typography variant="body2" sx={{ color: '#e6e9ec', lineHeight: 1.65, mb: 1 }}> | |
| {challenge.brief} | |
| </Typography> | |
| <Typography variant="caption" sx={{ color: '#8a949e' }}> | |
| Required stages:{' '} | |
| {challenge.required_stages.map((g) => g.join(' or ')).join(' · ')} | |
| {challenge.requires_feedback_loop ? ' · must close a feedback loop' : ''} | |
| </Typography> | |
| </> | |
| ) : null} | |
| </SectionCard> | |
| <Box sx={{ display: 'flex', gap: 2, flexWrap: 'wrap', alignItems: 'flex-start' }}> | |
| <SectionCard title={`Components (${components.length})`} sx={{ flex: '1 1 340px' }}> | |
| <Box sx={{ display: 'flex', gap: 1, mb: 1.5, flexWrap: 'wrap' }}> | |
| <TextField select size="small" label="Type" value={newType} onChange={(e) => setNewType(e.target.value)} sx={{ width: 130 }}> | |
| {(catalog.data?.component_types ?? []).map((t) => ( | |
| <MenuItem key={t} value={t}>{t}</MenuItem> | |
| ))} | |
| </TextField> | |
| <TextField size="small" label="Name" value={newName} onChange={(e) => setNewName(e.target.value)} sx={{ width: 150 }} /> | |
| <TextField size="small" label="Capacity note (e.g. 2 PB/week)" value={newNote} onChange={(e) => setNewNote(e.target.value)} sx={{ flex: 1, minWidth: 170 }} /> | |
| <Button size="small" variant="outlined" startIcon={<Plus size={14} />} onClick={addComponent}> | |
| Add | |
| </Button> | |
| </Box> | |
| {components.map((c) => ( | |
| <Box key={c.id} sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 0.5 }}> | |
| <Chip size="small" label={c.type} sx={{ bgcolor: TYPE_COLORS[c.type] ?? '#37474f', color: '#101418', fontWeight: 700, fontSize: 10.5, width: 88 }} /> | |
| <Typography variant="body2" sx={{ flex: 1 }}> | |
| {c.name} | |
| {c.note ? ( | |
| <Typography component="span" variant="caption" sx={{ color: '#8a949e', ml: 1 }}> | |
| {c.note} | |
| </Typography> | |
| ) : null} | |
| </Typography> | |
| <IconButton size="small" onClick={() => removeComponent(c.id)}> | |
| <Trash2 size={14} /> | |
| </IconButton> | |
| </Box> | |
| ))} | |
| </SectionCard> | |
| <SectionCard title={`Connections (${edges.length})`} sx={{ flex: '1 1 300px' }}> | |
| <Box sx={{ display: 'flex', gap: 1, mb: 1.5 }}> | |
| <TextField select size="small" label="From" value={edgeFrom} onChange={(e) => setEdgeFrom(e.target.value)} sx={{ flex: 1 }}> | |
| {components.map((c) => ( | |
| <MenuItem key={c.id} value={c.id}>{c.name}</MenuItem> | |
| ))} | |
| </TextField> | |
| <TextField select size="small" label="To" value={edgeTo} onChange={(e) => setEdgeTo(e.target.value)} sx={{ flex: 1 }}> | |
| {components.map((c) => ( | |
| <MenuItem key={c.id} value={c.id}>{c.name}</MenuItem> | |
| ))} | |
| </TextField> | |
| <Button size="small" variant="outlined" onClick={addEdge}> | |
| Connect | |
| </Button> | |
| </Box> | |
| {edges.map((e, i) => ( | |
| <Box key={i} sx={{ display: 'flex', alignItems: 'center', gap: 0.5, mb: 0.4 }}> | |
| <Typography variant="caption" sx={{ fontFamily: 'monospace', color: '#aab4be', flex: 1 }}> | |
| {nameOf(e.source)} → {nameOf(e.target)} | |
| </Typography> | |
| <IconButton size="small" onClick={() => setEdges(edges.filter((_x, j) => j !== i))}> | |
| <Trash2 size={13} /> | |
| </IconButton> | |
| </Box> | |
| ))} | |
| </SectionCard> | |
| </Box> | |
| <SectionCard title="Rationale per key decision" help="Each decision needs written reasoning. The grader scans for capacity math and coverage of the seven design dimensions — hand-waving scores zero."> | |
| {(challenge?.key_decisions ?? []).map((d) => ( | |
| <TextField | |
| key={d} | |
| label={d} | |
| multiline | |
| minRows={2} | |
| fullWidth | |
| sx={{ mb: 1.5 }} | |
| value={rationales[d] ?? ''} | |
| onChange={(e) => setRationales({ ...rationales, [d]: e.target.value })} | |
| /> | |
| ))} | |
| <Box sx={{ display: 'flex', justifyContent: 'flex-end' }}> | |
| <Button variant="contained" onClick={submit} disabled={busy || components.length === 0}> | |
| {busy ? 'Grading…' : 'Submit for grading'} | |
| </Button> | |
| </Box> | |
| {error ? <ErrorNote error={error} /> : null} | |
| </SectionCard> | |
| {grade ? ( | |
| <Box sx={{ display: 'flex', gap: 2, flexWrap: 'wrap' }}> | |
| <SectionCard | |
| title={ | |
| <Box component="span" sx={{ display: 'inline-flex', alignItems: 'center', gap: 1 }}> | |
| Structural checks <ScoreChip score={grade.overall_score} label="Overall" /> | |
| </Box> | |
| } | |
| sx={{ flex: '1 1 320px' }} | |
| > | |
| <Box sx={{ display: 'flex', gap: 0.75, flexWrap: 'wrap', mb: 1.5 }}> | |
| <PassChip passed={grade.structural.missing_stages.length === 0} label="required stages" /> | |
| <PassChip passed={grade.structural.orphan_components.length === 0} label="no orphans" /> | |
| <PassChip | |
| passed={!grade.structural.feedback_loop_required || grade.structural.feedback_loop_closed} | |
| label="feedback loop" | |
| /> | |
| <PassChip passed={grade.structural.single_points_of_failure.length === 0} label="no SPOF" /> | |
| <PassChip passed={grade.structural.capacity_math_found} label="capacity math" /> | |
| </Box> | |
| {grade.gaps.map((g, i) => ( | |
| <Typography key={i} variant="caption" sx={{ color: '#ef9a9a', display: 'block', mb: 0.5 }}> | |
| • {g} | |
| </Typography> | |
| ))} | |
| {grade.gaps.length === 0 ? ( | |
| <Typography variant="body2" sx={{ color: '#9ccc65' }}> | |
| No structural gaps found. | |
| </Typography> | |
| ) : null} | |
| </SectionCard> | |
| <SectionCard title="Per-dimension scores" sx={{ flex: '1 1 340px' }}> | |
| {grade.dimension_grades.map((d) => ( | |
| <HBar | |
| key={d.dimension} | |
| label={d.dimension.replace(/_/g, ' ')} | |
| value={d.score} | |
| max={5} | |
| color={d.score >= 3 ? '#66bb6a' : d.score >= 2 ? '#f9a825' : '#ef5350'} | |
| valueLabel={`${d.score}/5`} | |
| /> | |
| ))} | |
| {grade.evidence_id ? ( | |
| <Alert severity="success" variant="outlined" sx={{ mt: 1, py: 0.25 }}> | |
| Saved to your evidence library. | |
| </Alert> | |
| ) : null} | |
| </SectionCard> | |
| </Box> | |
| ) : null} | |
| </Box> | |
| ); | |
| } | |