Skip to content

Commit 98a925a

Browse files
committed
feat(h04): train-warm-start checkbox forwards continue_from_run_id
TopBar gains `train-warm-start` checkbox in the Train dropdown menu and threads a `warm_start: boolean` flag into `onRunPipeline("train", ...)`. App tracks `lastTrainRunId` (set after a Train stage completes with status="ok") and, when warm_start is on, includes `continue_from_run_id=lastTrainRunId` in stage_options.train. Backend V5-G10 (stages.py:770) already restored opt.state from the _RUN_CACHE on hit and emits extras.opt_state_carried=true. v6 closes the UI side so a user can actually exercise warm-start without editing JSON. Tests: - vbgui vitest TopBar.test.tsx: train-warm-start checkbox flips warm_start flag in onRunPipeline payload (default OFF → false, toggled ON → true) — 18/18 TopBar tests passing. - vbgui vitest full suite: 198/198 passing. - vbgui e2e 58_warm_start.spec.ts: two consecutive Train runs from the UI — first OFF asserts extras.opt_state_carried="false", second ON asserts extras.opt_state_carried="true". - vbgui e2e V6 suite (55+56+57+58): 5/5 passing — no regression. - backend pytest stage_train+warm: 40/40 passing.
1 parent 3ac2837 commit 98a925a

4 files changed

Lines changed: 94 additions & 4 deletions

File tree

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// H04: train-warm-start checkbox forwards continue_from_run_id so the
2+
// backend G10 LRU cache restores opt.state for the next Train run.
3+
//
4+
// First Train (warm-start OFF): cold opt.state → extras.opt_state_carried=false
5+
// Second Train (warm-start ON): same spec re-runs → opt_state_carried=true.
6+
//
7+
// The second run does not require a strictly lower losses[0] (the cached
8+
// opt.state pairs with a freshly built model whose weights re-randomise,
9+
// so the loss surface differs). What MUST hold is that the warm-start
10+
// flag actually flips the backend's opt_state_carried extras.
11+
12+
import { test, expect } from "@playwright/test";
13+
import { gotoApp, selectPreset, closeModal } from "../fixtures";
14+
import { readTrainExtras } from "../utils/train_extras";
15+
16+
test("H04: warm-start checkbox flips extras.opt_state_carried",
17+
async ({ page }) => {
18+
test.setTimeout(120_000);
19+
await gotoApp(page);
20+
await selectPreset(page, "llama3_8b");
21+
22+
// First Train — warm-start OFF (default). Expect opt_state_carried=false.
23+
await page.getByTestId("run-pipeline-toggle").click();
24+
await page.getByTestId("train-num-steps").fill("2");
25+
// Confirm the checkbox starts unchecked.
26+
await expect(page.getByTestId("train-warm-start")).not.toBeChecked();
27+
await page.getByTestId("run-pipeline-train").click();
28+
await page.getByTestId("run-result-modal").waitFor({ timeout: 60_000 });
29+
const firstExtras = await readTrainExtras(page);
30+
expect(firstExtras.losses.length).toBe(2);
31+
const firstCarried = await page.getByTestId(
32+
"run-result-extras-train-opt_state_carried").textContent();
33+
expect(firstCarried?.trim().toLowerCase()).toBe("false");
34+
await closeModal(page);
35+
36+
// Second Train — warm-start ON. Expect opt_state_carried=true.
37+
await page.getByTestId("run-pipeline-toggle").click();
38+
await page.getByTestId("train-warm-start").check();
39+
await expect(page.getByTestId("train-warm-start")).toBeChecked();
40+
await page.getByTestId("run-pipeline-train").click();
41+
await page.getByTestId("run-result-modal").waitFor({ timeout: 60_000 });
42+
const secondCarried = await page.getByTestId(
43+
"run-result-extras-train-opt_state_carried").textContent();
44+
expect(secondCarried?.trim().toLowerCase()).toBe("true");
45+
await closeModal(page);
46+
});

vbgui/src/App.tsx

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ export function App(): JSX.Element {
9494
const [runError, setRunError] = useState<string | null>(null);
9595
const [trainInFlight, setTrainInFlight] = useState(false);
9696
const [trainRunId, setTrainRunId] = useState<string | null>(null);
97+
// H04: most recent successfully-completed Train run_id, used as
98+
// continue_from_run_id when the warm-start checkbox is on. Cleared
99+
// when an error/cancel terminates the next run.
100+
const [lastTrainRunId, setLastTrainRunId] = useState<string | null>(null);
97101
const [selectedBrickId, setSelectedBrickId] = useState<string | null>(null);
98102
const [inferenceLog, setInferenceLog] = useState<
99103
{ brick: string; param: string; value: unknown;
@@ -289,7 +293,7 @@ export function App(): JSX.Element {
289293

290294
const handleRunPipeline = useCallback(async (
291295
mode: RunMode,
292-
opts?: { num_steps?: number },
296+
opts?: { num_steps?: number; warm_start?: boolean },
293297
) => {
294298
const snap = wireSpecRef.current;
295299
if (snap.nodes.length === 0) {
@@ -313,6 +317,11 @@ export function App(): JSX.Element {
313317
if (typeof opts?.num_steps === "number") {
314318
trainOpts.num_steps = opts.num_steps;
315319
}
320+
// H04: warm-start uses lastTrainRunId as continue_from_run_id so
321+
// the backend G10 LRU cache restores opt.state from prior run.
322+
if (opts?.warm_start && lastTrainRunId) {
323+
trainOpts.continue_from_run_id = lastTrainRunId;
324+
}
316325
if (trainParquetPath) trainOpts.parquet_path = trainParquetPath;
317326
if (trainTokenizerPath) trainOpts.tokenizer_path = trainTokenizerPath;
318327
// Forward SideChannelsTab train selection as synthetic int lists for the
@@ -336,6 +345,14 @@ export function App(): JSX.Element {
336345
pipeline: { stages, stage_options },
337346
});
338347
setRunReport(r);
348+
// H04: remember run_id of a successful Train so a follow-up
349+
// warm-start run can reference it.
350+
if (mode === "train" && activeTrainRunId) {
351+
const trainStage = r.stages?.find((s) => s.name === "train");
352+
if (trainStage?.status === "ok") {
353+
setLastTrainRunId(activeTrainRunId);
354+
}
355+
}
339356
} catch (e) {
340357
setRunError(String(e));
341358
} finally {
@@ -344,7 +361,8 @@ export function App(): JSX.Element {
344361
setTrainRunId(null);
345362
}
346363
}
347-
}, [rpc, trainParquetPath, trainSideChannels, trainTokenizerPath]);
364+
}, [rpc, trainParquetPath, trainSideChannels, trainTokenizerPath,
365+
lastTrainRunId]);
348366

349367
const handleCancelTrain = useCallback(async () => {
350368
const runId = trainRunId;

vbgui/src/components/TopBar.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export interface TopBarProps {
1414
onTopologyChange: (t: TopologyFactory) => void;
1515
onCompileModeChange: (m: SpecState["sharding"]["compile_mode"]) => void;
1616
onRunPipeline: (mode: RunMode,
17-
opts?: { num_steps?: number }) => void;
17+
opts?: { num_steps?: number; warm_start?: boolean }) => void;
1818
/** H02: toggle callbacks. */
1919
onMixedPrecisionChange?: (enabled: boolean) => void;
2020
onFp8EnabledChange?: (enabled: boolean) => void;
@@ -39,6 +39,7 @@ export interface TopBarProps {
3939
export function TopBar(p: TopBarProps): JSX.Element {
4040
const [open, setOpen] = useState(false);
4141
const [trainNumSteps, setTrainNumSteps] = useState<number>(2);
42+
const [warmStart, setWarmStart] = useState<boolean>(false);
4243
return (
4344
<header data-testid="top-bar"
4445
style={{ height: 56, display: "flex", alignItems: "center",
@@ -165,10 +166,19 @@ export function TopBar(p: TopBarProps): JSX.Element {
165166
e.target.value || "1", 10)))}
166167
style={{ width: 50 }} />
167168
</div>
169+
<label style={{ padding: "6px 12px", display: "flex",
170+
alignItems: "center", gap: 6, fontSize: 11,
171+
color: "#374151" }}>
172+
<input data-testid="train-warm-start" type="checkbox"
173+
checked={warmStart}
174+
onChange={(e) => setWarmStart(e.target.checked)} />
175+
warm-start (continue from last run)
176+
</label>
168177
<button data-testid="run-pipeline-train"
169178
onClick={() => { setOpen(false);
170179
p.onRunPipeline("train",
171-
{ num_steps: trainNumSteps }); }}
180+
{ num_steps: trainNumSteps,
181+
warm_start: warmStart }); }}
172182
disabled={!!p.trainDisabled}
173183
title={p.trainDisabled?.reason ?? ""}
174184
style={{ ...menuItem,

vbgui/tests/TopBar.test.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,22 @@ describe("TopBar", () => {
131131
expect.objectContaining({ num_steps: expect.any(Number) }));
132132
});
133133

134+
it("H04: train-warm-start checkbox forwards warm_start flag", () => {
135+
const onRunPipeline = vi.fn();
136+
render(<TopBar {...defaultTopProps({ onRunPipeline })} />);
137+
fireEvent.click(screen.getByTestId("run-pipeline-toggle"));
138+
// Default OFF → warm_start: false
139+
fireEvent.click(screen.getByTestId("run-pipeline-train"));
140+
expect(onRunPipeline).toHaveBeenLastCalledWith("train",
141+
expect.objectContaining({ warm_start: false }));
142+
// Toggle ON → warm_start: true
143+
fireEvent.click(screen.getByTestId("run-pipeline-toggle"));
144+
fireEvent.click(screen.getByTestId("train-warm-start"));
145+
fireEvent.click(screen.getByTestId("run-pipeline-train"));
146+
expect(onRunPipeline).toHaveBeenLastCalledWith("train",
147+
expect.objectContaining({ warm_start: true }));
148+
});
149+
134150
it("does not render legacy train side-channel checkboxes", () => {
135151
render(<TopBar {...defaultTopProps()} />);
136152
fireEvent.click(screen.getByTestId("run-pipeline-toggle"));

0 commit comments

Comments
 (0)