Fix training start NameError, the load-order guard test and CPU-only diffusion tests

- start_training forwards resume_source_run_id to _start_training_impl, which
  reads it. Without it every start raised NameError.
- Restore main's anchor in the load-marker order test: the file now has an
  earlier `if config.is_gguf:`, so indexing the first one compared the wrong
  branch.
- The two diffusion tests that reach diffusers now skip when it is absent,
  matching the CPU repo-test env.
- The UI smoke finds nav rows that live in the sidebar's More flyout.
This commit is contained in:
Unsloth 2026-07-25 19:18:35 -07:00
commit f06e8cf171
5 changed files with 36 additions and 4 deletions

View file

@ -864,16 +864,24 @@ class TrainingBackend:
return False
self._start_in_progress = True
try:
return self._start_training_impl(job_id, before_spawn = before_spawn, **kwargs)
return self._start_training_impl(
job_id,
before_spawn = before_spawn,
resume_source_run_id = resume_source_run_id,
**kwargs,
)
finally:
with self._lock:
self._start_in_progress = False
# Named, not part of **kwargs: the body reads it directly, and it must not reach the
# worker config either (start_training's own signature keeps it out).
def _start_training_impl(
self,
job_id: str,
*,
before_spawn = None,
resume_source_run_id: Optional[str] = None,
**kwargs,
) -> bool:
# Join prior pump thread — refuse to start if it won't die

View file

@ -29,7 +29,8 @@ def _qwen_scheduler():
# The Qwen/Qwen-Image scheduler config: shift=1.0 is SKIPPED at init because
# use_dynamic_shifting is true, base_shift = max_shift = log 3 (constant inference mu),
# exponential time shift, terminal stretch to 0.02.
from diffusers import FlowMatchEulerDiscreteScheduler
diffusers = pytest.importorskip("diffusers")
FlowMatchEulerDiscreteScheduler = diffusers.FlowMatchEulerDiscreteScheduler
return FlowMatchEulerDiscreteScheduler(
num_train_timesteps = 1000,
shift = 1.0,

View file

@ -550,6 +550,7 @@ def test_cast_fp8_is_idempotent_on_precast_encoder():
the engaged cast report as failed and status show no TE quant)."""
import torch
pytest.importorskip("diffusers") # _cast_fp8 installs diffusers' layerwise hooks
from core.inference.diffusion_precision import _cast_fp8
target = types.SimpleNamespace(dtype = torch.bfloat16)

View file

@ -785,7 +785,12 @@ class TestLoadHubDownloadExclusion:
def test_load_marker_precedes_hub_guard_and_unload(self):
source = (Path(__file__).resolve().parent.parent / "routes" / "inference.py").read_text()
gguf_branch = source[source.index("if config.is_gguf:") :]
# _load_model_impl has more than one `if config.is_gguf:`, so anchor on
# the branch that actually owns the load marker rather than the first
# one in the file, which belongs to an earlier check.
marker = source.index("enter_context(gguf_load_in_flight")
gguf_branch_start = source.rindex("if config.is_gguf:", 0, marker)
gguf_branch = source[gguf_branch_start:]
# The gguf_load_in_flight marker must be entered before the hub-download
# guard and the unload so a concurrent load can't race the download
@ -794,7 +799,7 @@ class TestLoadHubDownloadExclusion:
# inherited value (e.g. a carried --no-mmproj) shapes the guard's
# require_mmproj. Anchor on the call form so the assertion pins the
# endpoint's call site, not the function definition.
assert source.index("= _resolve_inherited_extra_args(") < source.index("if config.is_gguf:")
assert source.index("= _resolve_inherited_extra_args(") < gguf_branch_start
assert (
gguf_branch.index("enter_context(gguf_load_in_flight")
< gguf_branch.index("_hub_download_blocks_gguf_load")

View file

@ -1155,6 +1155,23 @@ with sync_playwright() as p:
if c.count() > 0:
btn = c
break
if btn is None:
# Unpinned rows (Video, Recipes, Export by default) live in the sidebar's
# "More" flyout. It opens on hover, so hover first; a click would toggle it
# straight back shut. Click is the fallback for a no-hover environment.
more_btn = page.get_by_role(
"button", name = re.compile(r"^\s*More\s*$", re.I)
).first
if more_btn.count() > 0:
more_btn.hover()
page.wait_for_timeout(500)
item = page.get_by_role("menuitem", name = re.compile(label, re.I)).first
if item.count() == 0:
more_btn.click(force = True)
page.wait_for_timeout(500)
item = page.get_by_role("menuitem", name = re.compile(label, re.I)).first
if item.count() > 0:
btn = item
if btn is None:
soft_fail(f"nav '{label}' not found")
return False