Training: Pausing and Stopping show beside the controls in Train, not with the graph in Results

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpnF7GwNGMbngU11wjym7P
This commit is contained in:
Claude
2026-10-17 21:23:04 +00:00
parent 7421333e2a
commit fb5c631c69
3 changed files with 12 additions and 2 deletions
+1
View File
@@ -182,6 +182,7 @@ export default {
async function stop() {
const waiting = ui.run.state === "paused" || ui.run.state === "previewing";
setRun({ message: "Stopping" });
refreshLive();
if (ui.run.state === "previewing") { ui.stopAfterPreview = true; try { app.generate.cancel(); } catch { /* it was over */ } }
try { await api.trainStop(); } catch { /* it ends anyway */ }
if (!waiting) { try { await fetch("/interrupt", { method: "POST" }); } catch { /* the training step check ends it */ } }
+5 -2
View File
@@ -97,6 +97,9 @@ export function trainStep({ app, session, go, ui }) {
].filter(Boolean) });
}
/** What the controls said last (Pausing, Stopping): shown beside the controls in Train, not with the graph in Results. */
const aboutControls = (message) => /^(Pausing|Stopping|It will wait)/.test(message || "");
/** What the Train step says about a run: where to look, and the error if it failed. */
export function notice(ui, go) {
const run = ui.run;
@@ -104,7 +107,7 @@ export function notice(ui, go) {
const where = c.button.sm({ label: "Open Results", tone: "ghost", onClick: () => go("results") });
if (run.error) return c.region.stack({ gap: "md", children: [c.inlineError.default({ text: run.error }), where] });
const text = { done: "Training finished.", stopped: "Training stopped.", paused: `Training paused at step ${run.step || 0}.`, previewing: "Making a preview." }[run.state] || "Training started.";
return c.region.stack({ gap: "md", children: [c.banner.info({ text: `${text} Check graphs and previews in the Results tab.` }), where] });
return c.region.stack({ gap: "md", children: [c.banner.info({ text: `${aboutControls(run.message) ? `${run.message}. ` : ""}${text} Check graphs and previews in the Results tab.` }), where] });
}
/** Progress, the message, and the loss graph. */
@@ -116,7 +119,7 @@ export function monitor(ui) {
const done = run.step || 0, total = run.total || 1;
const lines = [
run.state === "training" || run.state === "queued" ? `Step ${done} of ${total}${run.elapsed ? ` · ${clock(run.elapsed)}` : ""}${done && run.elapsed ? ` · about ${clock((run.elapsed / done) * (total - done))} left` : ""}` : null,
run.message || null,
run.message && !aboutControls(run.message) ? run.message : null,
].filter(Boolean);
const previews = ui.previews && ui.previews.length ? c.gallery.adaptive({ id: "training-previews", cols: 3, selection: [],
items: ui.previews.map((p) => ({ id: p.id, label: `Step ${p.step}`, thumb: stillOf(p), badge: p.media_kind })),
@@ -344,6 +344,12 @@ test("Pause asks the server, a paused run offers Resume, Preview now and Stop, a
click("Start training").click();
await wait(1400);
click("Pause").click();
await wait(20);
assert.ok(text().includes("Pausing at the end of this step"), "the controls' message is beside the controls");
tab("Results").click();
await wait(20);
assert.ok(!text().includes("Pausing at the end of this step"), "and not with the graph");
tab("Train").click();
await wait(1400);
assert.ok(f.calls.some((c) => c[0] === "pause"));
assert.ok(text().includes("paused at step 2"));