Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions frontend/src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ export interface LeaderboardDetail {
user_name: string;
submission_id: number;
line_count?: number;
validation_status?: "completed" | "failed" | null;
validation_shapes_passed?: number | null;
validation_shapes_total?: number | null;
validation_fully_validated?: boolean | null;
validation_geomean_speedup?: number | null;
validation_contract_version?: string | null;
validation_checked_at?: string | null;
}>
>;
}
Expand Down
81 changes: 71 additions & 10 deletions frontend/src/pages/leaderboard/Leaderboard.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ vi.mock("../../lib/hooks/useApi", () => ({
fetcherApiCallback: vi.fn(),
}));

vi.mock("./components/UserTrendChart", () => ({
default: () => <div data-testid="user-trend-chart" />,
}));

// Mutable auth state for mocking useAuthStore per test
type AuthState = {
me: null | { authenticated: boolean; user?: { identity?: string } };
Expand Down Expand Up @@ -108,6 +112,62 @@ describe("Leaderboard", () => {
expect(screen.getByText(/custom_kernel/)).toBeInTheDocument();
});

it("shows full and partial application validation badges beside submitters", () => {
const mockData = {
deadline: mockDeadline,
description: mockDescription,
name: mockName,
reference: mockReference,
starter: mockStarter,
gpu_types: ["B200"],
rankings: {
B200: [
{
file_name: "fast.py",
prev_score: 0,
rank: 1,
score: 3.25,
user_name: "stable-user",
submission_id: 101,
validation_status: "completed",
validation_shapes_passed: 8,
validation_shapes_total: 8,
validation_fully_validated: true,
validation_geomean_speedup: 1.4,
validation_contract_version: "v1",
},
{
file_name: "partial.py",
prev_score: 0.1,
rank: 2,
score: 3.5,
user_name: "partial-user",
submission_id: 102,
validation_status: "completed",
validation_shapes_passed: 6,
validation_shapes_total: 8,
validation_fully_validated: false,
validation_geomean_speedup: 1.1,
validation_contract_version: "v1",
},
],
},
};

(apiHook.fetcherApiCallback as ReturnType<typeof vi.fn>).mockReturnValue({
data: mockData,
loading: false,
error: null,
errorStatus: null,
call: mockCall,
});

renderWithRouter(<Leaderboard />);

expect(screen.getByText("VALIDATED")).toBeInTheDocument();
expect(screen.getByText("6/8 VALIDATED")).toBeInTheDocument();
});

it("shows loading state", () => {
(apiHook.fetcherApiCallback as ReturnType<typeof vi.fn>).mockReturnValue({
data: null,
Expand All @@ -118,7 +178,7 @@ describe("Leaderboard", () => {
});

renderWithRouter(<Leaderboard />);
expect(screen.getByText(/Summoning/i)).toBeInTheDocument();
expect(screen.getByRole("progressbar")).toBeInTheDocument();
});

it("shows error message", () => {
Expand Down Expand Up @@ -264,17 +324,18 @@ describe("Leaderboard", () => {
const btn = screen.queryByTestId("ranking-show-all-button-0");
expect(btn).toBeInTheDocument();

// By default only 3 rows shown
expect(screen.queryAllByTestId("ranking-0-row")).toHaveLength(3);

// Click to show all
fireEvent.click(btn!);
// Rankings start expanded.
expect(screen.queryAllByTestId("ranking-0-row")).toHaveLength(4);
expect(within(btn!).getByText(/Hide/i)).toBeInTheDocument();

// Click to hide again
// Hide the rows after the top three.
fireEvent.click(btn!);
expect(screen.queryAllByTestId("ranking-0-row")).toHaveLength(3);
expect(within(btn!).getByText(/Show all/i)).toBeInTheDocument();

// Expand again.
fireEvent.click(btn!);
expect(screen.queryAllByTestId("ranking-0-row")).toHaveLength(4);
});

// -------------------- Starter codeblock --------------------
Expand Down Expand Up @@ -510,9 +571,9 @@ describe("Leaderboard", () => {
// Switch to the Submission tab explicitly
fireEvent.click(screen.getByRole("tab", { name: /Submission/i }));

const submit_btn = screen.getByTestId("leaderboard-submit-btn");
expect(submit_btn).toBeInTheDocument();
expect(submit_btn).toBeDisabled();
expect(
screen.queryByTestId("leaderboard-submit-btn"),
).not.toBeInTheDocument();

const deadline_txt = screen.getByTestId("deadline-passed-text");
expect(
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/leaderboard/Leaderboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,8 @@ const LeaderboardContent = memo(function LeaderboardContent() {
findTopUsers();
}, [id, data?.rankings]);

if (loading || !data) return <Loading />;
if (error) return <ErrorAlert status={errorStatus} message={error} />;
if (loading || !data) return <Loading />;
if (!data) return null;

const toDeadlineLocal = (raw: string) => {
Expand Down
83 changes: 77 additions & 6 deletions frontend/src/pages/leaderboard/components/RankingLists.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import { useState } from "react";
import {
Box,
Button,
Chip,
Grid,
Stack,
Tooltip,
type SxProps,
type Theme,
Typography,
Expand All @@ -30,6 +32,13 @@ interface RankingItem {
submission_count?: number;
submission_time?: string;
line_count?: number;
validation_status?: "completed" | "failed" | null;
validation_shapes_passed?: number | null;
validation_shapes_total?: number | null;
validation_fully_validated?: boolean | null;
validation_geomean_speedup?: number | null;
validation_contract_version?: string | null;
validation_checked_at?: string | null;
}

interface RankingsListProps {
Expand Down Expand Up @@ -97,6 +106,58 @@ const styles: Record<string, SxProps<Theme>> = {
},
};

function ValidationBadge({ item }: { item: RankingItem }) {
if (!item.validation_status) return null;

const passed = item.validation_shapes_passed ?? 0;
const total = item.validation_shapes_total ?? 0;
const fullyValidated =
item.validation_status === "completed" &&
item.validation_fully_validated === true &&
total > 0 &&
passed === total;
const failed = item.validation_status === "failed";
const label = fullyValidated
? "VALIDATED"
: failed
? "VALIDATION ERROR"
: `${passed}/${total} VALIDATED`;
const speedup =
typeof item.validation_geomean_speedup === "number"
? ` Geomean synchronized-wall speedup across measured shapes: ${item.validation_geomean_speedup.toFixed(2)}×.`
: "";
const contract = item.validation_contract_version
? ` Contract: ${item.validation_contract_version}.`
: "";
const checked = item.validation_checked_at
? ` Checked ${new Date(item.validation_checked_at).toLocaleString()}.`
: "";
const tooltip = fullyValidated
? `All ${passed}/${total} training shapes passed the convergence, numerical, and speed gates.${speedup}${contract}${checked}`
: failed
? `The validation job failed before it could complete.${contract}${checked}`
: `${passed}/${total} training shapes passed the convergence, numerical, and speed gates.${speedup}${contract}${checked}`;

return (
<Tooltip title={tooltip}>
<Chip
label={label}
color={fullyValidated ? "success" : failed ? "error" : "warning"}
size="small"
variant={fullyValidated ? "filled" : "outlined"}
data-testid={`validation-badge-${item.submission_id}`}
sx={{
height: 20,
fontSize: "0.65rem",
fontWeight: 800,
letterSpacing: "0.03em",
flexShrink: 0,
}}
/>
</Tooltip>
);
}

export default function RankingsList({
rankings,
leaderboardId,
Expand All @@ -114,7 +175,7 @@ export default function RankingsList({
const toggleExpanded = (field: string) => {
setExpanded((prev) => ({
...prev,
[field]: !prev[field],
[field]: !(prev[field] ?? true),
}));
};

Expand Down Expand Up @@ -205,9 +266,19 @@ export default function RankingsList({
<Grid size={3}>
<Box sx={{ display: "flex", alignItems: "center", gap: 1 }}>
<Typography sx={styles.rank}>{item.rank}. </Typography>
<Typography sx={styles.name}>
{item.user_name} {getMedalIcon(item.rank)}
</Typography>
<Box
sx={{
display: "flex",
alignItems: "center",
gap: 0.75,
minWidth: 0,
}}
>
<Typography sx={styles.name}>
{item.user_name} {getMedalIcon(item.rank)}
</Typography>
<ValidationBadge item={item} />
</Box>
</Box>
</Grid>
<Grid size={scoreSize}>
Expand All @@ -222,7 +293,7 @@ export default function RankingsList({
</Typography>
</Grid>
<Grid size={isAdmin ? 2 : fileSize}>
<Typography
<Box
sx={{
overflow: "hidden",
textOverflow: "ellipsis",
Expand Down Expand Up @@ -253,7 +324,7 @@ export default function RankingsList({
{item.file_name}
</Button>
)}
</Typography>
</Box>
</Grid>
{showLineCount && (
<Grid size={isAdmin ? 1 : 2}>
Expand Down
36 changes: 35 additions & 1 deletion kernelboard/api/leaderboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,11 +265,38 @@ def _get_query():
s.file_name AS file_name,
r.submission_id AS submission_id,
COALESCE(sc.submission_count, 0) AS submission_count,
validation.status AS validation_status,
validation.passed_shapes AS validation_shapes_passed,
validation.total_shapes AS validation_shapes_total,
validation.fully_validated AS validation_fully_validated,
validation.geomean_sync_wall_speedup AS validation_geomean_speedup,
validation.contract_version AS validation_contract_version,
validation.checked_at AS validation_checked_at,
RANK() OVER (PARTITION BY r.runner, u.id ORDER BY r.score ASC) AS rank
FROM leaderboard.runs r
JOIN leaderboard.submission s ON r.submission_id = s.id
LEFT JOIN leaderboard.user_info u ON s.user_id = u.id
LEFT JOIN submission_counts sc ON s.user_id = sc.user_id AND r.runner = sc.runner
LEFT JOIN LATERAL (
SELECT
status,
passed_shapes,
total_shapes,
fully_validated,
geomean_sync_wall_speedup,
contract_version,
checked_at
FROM leaderboard.submission_validation
WHERE submission_id = s.id
AND gpu_type = r.runner
AND contract_version = (
SELECT task->'validation'->>'version'
FROM leaderboard.leaderboard
WHERE id = %(leaderboard_id)s
)
ORDER BY checked_at DESC
LIMIT 1
) validation ON TRUE
WHERE NOT r.secret
AND r.score IS NOT NULL
AND r.passed
Expand Down Expand Up @@ -312,7 +339,14 @@ def _get_query():
'file_name', r.file_name,
'submission_id', r.submission_id,
'submission_count', r.submission_count,
'submission_time', r.submission_time
'submission_time', r.submission_time,
'validation_status', r.validation_status,
'validation_shapes_passed', r.validation_shapes_passed,
'validation_shapes_total', r.validation_shapes_total,
'validation_fully_validated', r.validation_fully_validated,
'validation_geomean_speedup', r.validation_geomean_speedup,
'validation_contract_version', r.validation_contract_version,
'validation_checked_at', r.validation_checked_at
)
ORDER BY r.score ASC
)
Expand Down
Loading
Loading