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:
parent
917245e34a
commit
f06e8cf171
5 changed files with 36 additions and 4 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue