From c485c4f5df99bf8b5befb1f037fda4bae3122a0b Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 17 Oct 2026 22:16:12 +0000 Subject: [PATCH] Training: the preset you pick stays picked when you change a setting after it Co-Authored-By: Claude Sonnet 5.5 Claude-Session: https://claude.ai/code/session_01MpnF7GwNGMbngU11wjym7P --- app/ui/features/training/index.js | 1 + app/ui/features/training/train.js | 4 ++-- app/ui/features/training/training.test.js | 24 +++++++++++++++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/app/ui/features/training/index.js b/app/ui/features/training/index.js index b3145fd4..44e94874 100644 --- a/app/ui/features/training/index.js +++ b/app/ui/features/training/index.js @@ -113,6 +113,7 @@ export default { if (!doc) return; await session.flush(); const settings = settingsOf(doc); + delete settings.preset; // a starting point the person picked, not a setting of the trainer settings.trigger = doc.trigger || ""; settings.postfix = doc.postfix || ""; if (resumeId) settings.resume = resumeId; diff --git a/app/ui/features/training/train.js b/app/ui/features/training/train.js index 62ecb738..6f4f20b6 100644 --- a/app/ui/features/training/train.js +++ b/app/ui/features/training/train.js @@ -38,9 +38,9 @@ export function trainStep({ app, session, go, ui }) { const caption = used.filter((i) => i.caption.trim()).length; const name = c.field.default({ label: "Name of the LoRA", hint: "Saved in your loras folder. An earlier file is never overwritten.", control: c.input.md({ label: "Name", value: v.name, onCommit: set("name") }) }); - const preset = c.segmented.sm({ label: "Preset", options: PRESETS, value: (PRESETS.find((p) => p.steps === v.steps && p.rank === v.rank) || {}).value, onChange: (key) => { + const preset = c.segmented.sm({ label: "Preset", options: PRESETS, value: v.preset || (PRESETS.find((p) => p.steps === v.steps && p.rank === v.rank) || {}).value, onChange: (key) => { const p = PRESETS.find((x) => x.value === key); - session.edit((d) => { d.settings = { ...(d.settings || {}), steps: p.steps, rank: p.rank }; }); + session.edit((d) => { d.settings = { ...(d.settings || {}), preset: key, steps: p.steps, rank: p.rank }; }); ui.redraw(); } }); diff --git a/app/ui/features/training/training.test.js b/app/ui/features/training/training.test.js index 912f2038..0f16cce9 100644 --- a/app/ui/features/training/training.test.js +++ b/app/ui/features/training/training.test.js @@ -386,3 +386,27 @@ test("Restore and continue is offered for a failed run, goes away while it carri assert.ok(click("Restore and continue") && !click("Restore and continue").hidden, "offered again after the next failure"); off(); }); + +test("Train step: the preset picked stays picked when a setting is changed after it, and is not sent to the trainer", async () => { + const f = fake(); + const { button, off } = await open(f); + button.click(); + await wait(60); + click("Skip to training").click(); + await wait(20); + click("Thorough").click(); + await wait(20); + const rank = [...document.querySelectorAll(".cx-field")].find((x) => x.textContent.startsWith("Rank")).querySelector("input"); + rank.value = "8"; + rank.dispatchEvent(new window.Event("change", { bubbles: true })); + await wait(700); + const picked = [...document.querySelectorAll("[aria-pressed=true], [aria-checked=true], .is-active, .on")].map((e) => e.textContent.trim()); + assert.ok(picked.includes("Thorough") && !picked.includes("Quick check"), `picked: ${picked}`); + click("Start training").click(); + await wait(60); + const plan = f.calls.find((c) => c[0] === "plan"); + assert.equal(plan[2].rank, 8); + assert.equal(plan[2].steps, 2000); + assert.ok(!("preset" in plan[2])); + off(); +});