township: the Train button never built a LongCat dataset (v0.2.50)

The log shows the 401 is gone — the latest attempt was accepted (200 OK,
job-551586975a13) and reached the trainer. It then failed on the SAME error as
before the key was fixed:

  RuntimeError: longcat LoRA training needs `dataset_config`

Because there are two training paths and only one of them was wired. Step 4b (the
batch stage) builds a SimpleTuner dataset from the rendered clips. The per-profile
Train button on the Characters page — which is what was being pressed — assembled
its kwargs independently and never passed dataset_config, so the server refused
the job exactly as it is supposed to.

Both paths now build the same dataset through the same function. When it cannot
(no clips rendered yet, clips too short, too few), the button says so and names
the remedy instead of letting the server answer with a stack trace.

Surveyed against the real township output before claiming it works: 151 clips and
outcomes carry prompts, and at the default 93-frame / 8-clip threshold all 6
fighters and all 4 environments qualify — khumalo has 19 usable clips, dlamini 11.

Still true, and unchanged by this: no LongCat training has ever RUN. This gets the
job past the server's gate; what SimpleTuner then does with it is unproven.
Co-Authored-By: 's avatarClaude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0182DhR1eFNPQyGmDrpHbedY
parent 19aded5f
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
# Canonical product version for CoderAI — single source of truth. Both the API # Canonical product version for CoderAI — single source of truth. Both the API
# metadata and the admin web UI read from here. # metadata and the admin web UI read from here.
__version__ = "0.2.49" __version__ = "0.2.50"
__tagline__ = "Complete orchestration, distribution and escalation of remotizable advanced inference." __tagline__ = "Complete orchestration, distribution and escalation of remotizable advanced inference."
# Configure the CUDA caching allocator BEFORE torch is imported anywhere. # Configure the CUDA caching allocator BEFORE torch is imported anywhere.
......
...@@ -164,3 +164,23 @@ def test_the_default_frame_count_obeys_the_vae_rule(): ...@@ -164,3 +164,23 @@ def test_the_default_frame_count_obeys_the_vae_rule():
]) ])
def test_longcat_detection(model, expected): def test_longcat_detection(model, expected):
assert TW._is_longcat_model(model) is expected assert TW._is_longcat_model(model) is expected
def test_both_training_paths_build_a_dataset_for_longcat():
"""The batch stage (step 4b) and the per-profile Train button are separate code
paths. Only the first built a dataset, so training one fighter from the
Characters page still reached the server with no dataset_config and was
refused — the same error, from the button nobody had wired.
"""
src = (ROOT / "tools" / "gen_township_fighters.py").read_text(encoding="utf-8")
# the batch stage
assert "dataset_config=(str(dataset) if dataset else None)" in src
# the per-profile button
assert 'kwargs["dataset_config"] = str(_ds)' in src
assert src.count("build_video_dataset(") >= 3 # definition + both call sites
def test_the_per_profile_path_explains_what_to_do_when_it_cannot_build_one():
src = (ROOT / "tools" / "gen_township_fighters.py").read_text(encoding="utf-8")
assert "cannot train a LongCat video LoRA" in src
assert "Render some match clips first" in src
...@@ -6083,6 +6083,31 @@ def launch_web_ui(default_args): ...@@ -6083,6 +6083,31 @@ def launch_web_ui(default_args):
wait=False, session=session) wait=False, session=session)
if is_video: if is_video:
kwargs["target"] = "video" kwargs["target"] = "video"
# LongCat trains through SimpleTuner, which needs a captioned
# VIDEO dataset. The batch stage (step 4b) builds one from the
# rendered clips; this per-profile button has to do the same, or
# the server refuses the job — which is exactly what it did.
if _is_longcat_model(model):
try:
_res = (getattr(default_args, "video_size", "832x480")
or "832x480").lower().split("x")
_short = min(int(_res[0]), int(_res[1]))
except Exception:
_short = 480
_ds, _why = build_video_dataset(
out_dir, kind, name, slug,
num_frames=getattr(default_args, "dataset_frames",
DATASET_FRAMES),
resolution=_short,
min_clips=getattr(default_args, "dataset_min_clips",
DATASET_MIN_CLIPS))
if _ds is None:
raise RuntimeError(
f"cannot train a LongCat video LoRA for '{name}': {_why}. "
f"Render some match clips first (step 6) — the dataset is "
f"built from the clips and the prompts they came from.")
_web_log(f" dataset: {_why} → {_ds}")
kwargs["dataset_config"] = str(_ds)
else: else:
_tbm = getattr(default_args, "lora_train_base_model", None) or None _tbm = getattr(default_args, "lora_train_base_model", None) or None
if _tbm: if _tbm:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment