import React, { useState } from "react";
import {
Box,
Paper,
Typography,
TextField,
Button,
FormControl,
FormLabel,
RadioGroup,
FormControlLabel,
Radio,
InputLabel,
Select,
MenuItem,
Stack,
Grid,
CircularProgress,
Alert,
} from "@mui/material";
import RocketLaunchIcon from "@mui/icons-material/RocketLaunch";
import CheckCircleOutlineIcon from "@mui/icons-material/CheckCircleOutline";
import { alpha } from "@mui/material/styles";
import InfoIconWithTooltip from "../../../../components/shared/InfoIconWithTooltip";
import {
FINETUNING_STRATEGIES,
HEADS,
PEFT_STRATEGIES,
OEB_DATASETS,
} from "../../../../pages/LeaderboardPage/components/Leaderboard/constants/modelTypes";
import AuthContainer from "../../../../components/shared/AuthContainer";
const HELP_TEXTS = {
modelClass: (
Model Class (dotted import path)
Fully-qualified import path to the model class evaluated by
OpenEEGBench. The class must accept (batch, n_chans, n_times) and
return (batch, n_outputs). Example: braindecode.models.BIOT
),
modelName: (
Model Name
A human-readable name for your submission. This is how your model will
appear on the leaderboard.
),
weightsSource: (
Weights Source
Provide exactly one source for the pre-trained weights: a public
Hugging Face Hub repository (with a license tag) or a direct
http(s) URL to a checkpoint.
),
headModuleName: (
Head Module Name
Name of the model's classification head module. OpenEEGBench replaces
this module to attach evaluation heads. Default: final_layer
),
finetuningStrategies: (
Fine-tuning Strategies
Frozen: linear probing (encoder frozen). LoRA / IA3 / AdaLoRA / DoRA /
OFT: parameter-efficient fine-tuning. Full fine-tune: all parameters
updated. Two stages: probe then fine-tune. Select one or more.
),
heads: (
Evaluation Heads
Linear head: single linear layer. MLP head: small multi-layer
perceptron. Original head: keep the model's own head. Select one or
more.
),
peftTargetModules: (
PEFT Target Modules
Comma-separated module names to which the PEFT adapter is applied
(e.g. to_q, to_v). Required when a PEFT strategy (LoRA, IA3, AdaLoRA,
DoRA, OFT) is selected.
),
peftFfModules: (
PEFT Feed-Forward Modules
Comma-separated feed-forward module names scaled by IA3. Required when
the IA3 strategy is selected.
),
datasets: (
Datasets
The downstream datasets to evaluate on. Leave empty to run all 10
datasets in the OpenEEGBench classification suite.
),
nSeeds: (
Number of Seeds
How many random seeds to average over per dataset. More seeds give a
more stable estimate but take longer. Default: 3
),
contactEmail: (
Contact Email
Optional. Stored privately and used only to reach you about your
submission. It is never shown on the public leaderboard.
),
visibility: (
Visibility
Public submissions appear on the leaderboard. Private submissions are
evaluated but kept out of the public table.
),
};
const VISIBILITY_OPTIONS = ["Public", "Private"];
const INITIAL_FORM_DATA = {
modelClass: "", // dotted import path
modelName: "",
organization: "",
weightsSource: "hub_repo", // "hub_repo" | "checkpoint_url"
hubRepo: "",
checkpointUrl: "",
headModuleName: "final_layer",
finetuningStrategies: ["frozen"],
heads: ["linear_head"],
peftTargetModules: "", // comma-separated -> array on submit
peftFfModules: "", // comma-separated -> array on submit
datasets: [], // empty = all
nSeeds: 3,
modelUrl: "",
paperUrl: "",
contactEmail: "",
additionalInfo: "",
visibility: "Public",
};
const toList = (s) =>
s
.split(",")
.map((x) => x.trim())
.filter(Boolean);
const strategyLabel = (value) =>
FINETUNING_STRATEGIES.find((s) => s.value === value)?.label || value;
const headLabel = (value) =>
HEADS.find((h) => h.value === value)?.label || value;
function ModelSubmissionForm({ user, isAuthenticated }) {
const [formData, setFormData] = useState(INITIAL_FORM_DATA);
const [error, setError] = useState(null);
const [submitting, setSubmitting] = useState(false);
const [success, setSuccess] = useState(false);
const [submittedData, setSubmittedData] = useState(null);
const showPeftTargetModules = formData.finetuningStrategies.some((s) =>
PEFT_STRATEGIES.includes(s)
);
const showPeftFfModules = formData.finetuningStrategies.includes("ia3");
const handleChange = (event) => {
const { name, value, checked } = event.target;
setFormData((prev) => ({
...prev,
[name]: event.target.type === "checkbox" ? checked : value,
}));
};
const handleSubmit = async (e) => {
e.preventDefault();
setError(null);
setSubmitting(true);
try {
const body = {
user_id: user.username,
model_cls: formData.modelClass,
model_name: formData.modelName,
organization: formData.organization,
hub_repo:
formData.weightsSource === "hub_repo" ? formData.hubRepo : "",
checkpoint_url:
formData.weightsSource === "checkpoint_url"
? formData.checkpointUrl
: "",
head_module_name: formData.headModuleName,
finetuning_strategies: formData.finetuningStrategies,
heads: formData.heads,
peft_target_modules: toList(formData.peftTargetModules),
peft_ff_modules: toList(formData.peftFfModules),
datasets: formData.datasets,
n_seeds: Number(formData.nSeeds),
model_url: formData.modelUrl,
paper_url: formData.paperUrl,
contact_email: formData.contactEmail,
additional_info: formData.additionalInfo,
visibility: formData.visibility,
};
const response = await fetch("/api/models/submit", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || "Failed to submit model");
}
setSubmittedData(formData);
setSuccess(true);
} catch (error) {
setError(error.message);
} finally {
setSubmitting(false);
}
};
if (success && submittedData) {
return (
({
p: 6,
mb: 3,
bgcolor: alpha(theme.palette.success.main, 0.05),
borderColor: alpha(theme.palette.success.main, 0.2),
})}
>
Model submitted successfully!
Your model {submittedData.modelName} has been added
to the evaluation queue with the following parameters:
Model:
{submittedData.modelName}
Model class:
{submittedData.modelClass}
Strategies:
{submittedData.finetuningStrategies
.map(strategyLabel)
.join(", ")}
Visibility:
{submittedData.visibility}
A worker will run oeb.benchmark() and post the official scores to
the leaderboard.
);
}
return (
<>
{error && (
{error}
)}
{isAuthenticated && (
{/* Header */}
theme.palette.mode === "dark"
? alpha(theme.palette.divider, 0.1)
: "grey.200",
bgcolor: (theme) =>
theme.palette.mode === "dark"
? alpha(theme.palette.background.paper, 0.5)
: "grey.50",
}}
>
Model Submission Form
{/* Form Content */}
{/* Model Information */}
Model Information
),
}}
/>
),
}}
/>
{/* Weights Source */}
Weights Source
Provide one weights source
}
label="Hugging Face Hub repo"
/>
}
label="Checkpoint URL"
/>
{formData.weightsSource === "hub_repo" ? (
) : (
)}
),
}}
/>
{/* Evaluation Configuration */}
Evaluation Configuration
Fine-tuning Strategies
Heads
{showPeftTargetModules && (
),
}}
/>
)}
{showPeftFfModules && (
),
}}
/>
)}
Datasets
),
}}
/>
{/* Additional Information */}
Additional Information
),
}}
/>
Visibility
}
>
{VISIBILITY_OPTIONS.map((option) => (
))}
{/* Submit Button */}
All fields marked with * are required
}
sx={{
minWidth: 120,
position: "relative",
}}
>
{submitting ? (
) : (
"Submit"
)}
)}
>
);
}
export default ModelSubmissionForm;