From f0a4f77028bbf30c217e8eeb18b1997d9d40460e Mon Sep 17 00:00:00 2001 From: Shine1i Date: Tue, 17 Feb 2026 20:08:02 +0100 Subject: [PATCH] feat: enhance training flow with new runtime hints, adjustable steps/epochs - Added halfway/completed training hints with actionable links. - Introduced sliders for adjusting max steps and epochs dynamically. - Refined tooltip explanations for configuration parameters. - Enabled custom overlay styling for `AlertDialogContent`. --- .../src/components/ui/alert-dialog.tsx | 28 +++---- .../components/steps/hyperparameters-step.tsx | 78 ++++++++++++++++--- .../studio/sections/params-section.tsx | 50 ++++++------ .../studio/sections/progress-section.tsx | 26 ++++++- 4 files changed, 132 insertions(+), 50 deletions(-) diff --git a/studio/frontend/src/components/ui/alert-dialog.tsx b/studio/frontend/src/components/ui/alert-dialog.tsx index b39a310c49..61327e525e 100644 --- a/studio/frontend/src/components/ui/alert-dialog.tsx +++ b/studio/frontend/src/components/ui/alert-dialog.tsx @@ -42,19 +42,21 @@ function AlertDialogOverlay({ ); } -function AlertDialogContent({ - className, - size = "default", - ...props -}: React.ComponentProps & { - size?: "default" | "sm"; -}) { - return ( - - - & { + size?: "default" | "sm"; + overlayClassName?: string; +}) { + return ( + + + ({ trainingMethod: s.trainingMethod, + maxSteps: s.maxSteps, + setMaxSteps: s.setMaxSteps, epochs: s.epochs, setEpochs: s.setEpochs, contextLength: s.contextLength, @@ -60,6 +64,8 @@ export function HyperparametersStep() { const showLoraParams = trainingMethod === "lora" || trainingMethod === "qlora"; + const maxStepsSliderMax = Math.max(500, maxSteps, 30); + const epochsSliderMax = Math.max(10, epochs, 1); return ( @@ -68,7 +74,7 @@ export function HyperparametersStep() {
- Epochs + Max Steps
@@ -196,6 +202,56 @@ export function HyperparametersStep() { className="w-32 font-mono" />
+ +
+ + Epochs + + + + + + Number of full passes over the dataset. Set 0 to run by max + steps.{" "} + + Read more + + + + +
+ setEpochs(v)} + min={0} + max={epochsSliderMax} + step={1} + className="w-40" + /> + setEpochs(Number(e.target.value))} + min={0} + max={epochsSliderMax} + step={1} + className="w-12 text-right font-mono text-xs font-medium bg-muted/50 border border-border rounded-lg px-1.5 py-0.5 focus:outline-none focus:ring-1 focus:ring-primary/30 [&::-webkit-inner-spin-button]:appearance-none" + /> +
+
diff --git a/studio/frontend/src/features/studio/sections/params-section.tsx b/studio/frontend/src/features/studio/sections/params-section.tsx index 141a38286d..2d7968e6c5 100644 --- a/studio/frontend/src/features/studio/sections/params-section.tsx +++ b/studio/frontend/src/features/studio/sections/params-section.tsx @@ -112,6 +112,8 @@ export function ParamsSection(): ReactElement { const showVisionLora = store.isVisionModel && store.isDatasetMultimodal === true; const [loraOpen, setLoraOpen] = useState(false); const [hyperOpen, setHyperOpen] = useState(false); + const maxStepsSliderMax = Math.max(500, store.maxSteps, 30); + const epochsSliderMax = Math.max(20, store.epochs, 1); return (
@@ -123,11 +125,11 @@ export function ParamsSection(): ReactElement { className="min-h-[450px]" >
- {/* Epochs */} + {/* Max Steps */}
store.setEpochs(v)} - min={1} - max={20} + value={[Math.min(maxStepsSliderMax, Math.max(0, store.maxSteps))]} + onValueChange={([v]) => store.setMaxSteps(v)} + min={0} + max={maxStepsSliderMax} step={1} />

- Number of full passes over the training dataset + Total optimizer steps. Use 0 to run by epochs.

@@ -602,11 +603,12 @@ export function ParamsSection(): ReactElement { max={100} step={1} /> - - Override total steps. 0 means use epochs instead.{" "} + Number of full passes over the dataset. Set 0 to run by + max steps.{" "} } - > - store.setMaxSteps(Number(e.target.value))} - className="w-28 font-mono" - /> - + value={store.epochs} + onChange={store.setEpochs} + min={0} + max={epochsSliderMax} + step={1} + /> 0 ? runtime.currentStep / elapsed : null; + const showHalfwayHint = + runtime.phase === "training" && pct >= 50 && pct < 100; + const showCompletedHint = runtime.phase === "completed"; const stoppedLoss = getDisplayMetric( runtime.isTrainingRunning, @@ -198,7 +202,7 @@ export function ProgressSection(): ReactElement { > Stop - + Stop Training @@ -252,6 +256,26 @@ export function ProgressSection(): ReactElement {
+ {(showHalfwayHint || showCompletedHint) && ( +
+

+ {showCompletedHint + ? "Training done. Next step: compare base vs fine-tuned outputs." + : "Halfway done. Training is past 50%."} +

+ {showCompletedHint && ( +
+ + +
+ )} +
+ )} + {runtime.error && (

{runtime.error}

)}