| """ |
| sampler_sweep.py β Same A/B experiment as sampler_ab.py, but over EVERY |
| sampler ComfyUI has installed EXCEPT the ones already tested |
| (er_sde, flow_euler, euler). One character, fixed seeds, only the sampler |
| varies. Each sampler's outputs get a zero-padded index PREFIX (s01_, s02_β¦) |
| so they're easy to identify and sort in the folder and the report. |
| |
| .venv/bin/python3 tools/sampler_sweep.py # yuki, 3 seeds |
| .venv/bin/python3 tools/sampler_sweep.py --char sakura --tries 3 |
| .venv/bin/python3 tools/sampler_sweep.py --exclude er_sde flow_euler euler dpmpp_2m |
| |
| VRAM: stops the LLM first (frees the GPU for ComfyUI). Restart it after. |
| |
| Output: static/sprites/_sampler_test/<char>_sweep/sNN_<sampler>_seed<seed>.png |
| static/sprites/_sampler_test/<char>_sweep/REPORT.md |
| """ |
| import argparse |
| import os |
| import sys |
|
|
| sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) |
|
|
| from cast_pipeline import ( |
| PRESET_CHARACTERS, SPRITE_NEGATIVE, SCHEDULER, GENERATED_DIR, |
| _comfy_health, _comfy_submit, _comfy_wait, _comfy_fetch_image, COMFY_HOST, |
| ) |
| |
| from sampler_ab import _valid_samplers, _base_workflow, STEPS, CFG, WIDTH, HEIGHT, TEST_DIR |
|
|
| |
| DEFAULT_EXCLUDE = ["er_sde", "flow_euler", "euler"] |
|
|
|
|
| def main() -> int: |
| ap = argparse.ArgumentParser(description="Sweep every other sampler on one character's base gen") |
| ap.add_argument("--char", default="yuki", help="preset character key (default: yuki)") |
| ap.add_argument("--tries", type=int, default=3, help="seeds per sampler (default: 3)") |
| ap.add_argument("--exclude", nargs="+", default=DEFAULT_EXCLUDE, |
| help=f"samplers to skip (default: {DEFAULT_EXCLUDE})") |
| args = ap.parse_args() |
|
|
| preset = PRESET_CHARACTERS.get(args.char) |
| if not preset: |
| print(f"Unknown character '{args.char}'. Options: {list(PRESET_CHARACTERS)}") |
| return 1 |
|
|
| if not _comfy_health(): |
| print(f"ComfyUI not reachable at {COMFY_HOST}. Start it first.") |
| return 1 |
|
|
| valid = _valid_samplers() |
| if not valid: |
| print("Couldn't read ComfyUI's sampler list β needed to enumerate the sweep. Aborting.") |
| return 1 |
|
|
| excluded = set(args.exclude) |
| samplers = sorted(s for s in valid if s not in excluded) |
| if not samplers: |
| print("No samplers left after exclusions.") |
| return 1 |
|
|
| |
| try: |
| from model_client import release_vram |
| release_vram() |
| except Exception as e: |
| print(f"[sweep] release_vram failed ({e}); continuing") |
|
|
| base_seed = preset["seed"] |
| seeds = [base_seed + i for i in range(args.tries)] |
| out_dir = os.path.join(TEST_DIR, f"{args.char}_sweep") |
| os.makedirs(out_dir, exist_ok=True) |
| prompt, negative = preset["identity_prompt"], SPRITE_NEGATIVE |
|
|
| print(f"\n[sweep] {args.char}: {len(samplers)} sampler(s) Γ {len(seeds)} seed(s)") |
| print(f"[sweep] excluded: {sorted(excluded)}") |
| print(f"[sweep] sweeping: {samplers}\n") |
|
|
| |
| labels = {s: f"s{i + 1:02d}" for i, s in enumerate(samplers)} |
|
|
| grid = {} |
| for sampler in samplers: |
| pfx = labels[sampler] |
| for seed in seeds: |
| tag = f"{pfx}_{sampler}_seed{seed}" |
| print(f" [{pfx}] {sampler} seed {seed}β¦", end=" ", flush=True) |
| dest = os.path.join(out_dir, f"{tag}.png") |
| try: |
| pid = _comfy_submit(_base_workflow(prompt, negative, seed, sampler), |
| f"sweep_{args.char}_{tag}") |
| result = _comfy_wait(pid) |
| ok = bool(result and _comfy_fetch_image(result, dest)) |
| except Exception as e: |
| print(f"FAILED ({e})") |
| grid[(sampler, seed)] = None |
| continue |
| print("ok" if ok else "no output") |
| grid[(sampler, seed)] = f"{tag}.png" if ok else None |
|
|
| |
| lines = [f"# Sampler sweep β {args.char}", "", |
| f"Fixed: scheduler `{SCHEDULER}`, steps {STEPS}, cfg {CFG}, " |
| f"{WIDTH}Γ{HEIGHT}. Only the sampler varies. " |
| f"Excluded (see sampler_ab.py): {sorted(excluded)}.", "", |
| "| # | sampler | " + " | ".join(f"seed {s}" for s in seeds) + " |", |
| "|---|" + "---|" * (len(seeds) + 1)] |
| for sampler in samplers: |
| cells = [] |
| for s in seeds: |
| f = grid.get((sampler, s)) |
| cells.append(f"" if f else "β") |
| lines.append(f"| {labels[sampler]} | **{sampler}** | " + " | ".join(cells) + " |") |
| with open(os.path.join(out_dir, "REPORT.md"), "w") as fp: |
| fp.write("\n".join(lines) + "\n") |
|
|
| done = sum(1 for v in grid.values() if v) |
| print(f"\n[sweep] Done β {done}/{len(grid)} renders. Images + REPORT.md in: {out_dir}") |
| print("[sweep] LLM was stopped β restart it (or run app.py) before playing.") |
| return 0 |
|
|
|
|
| if __name__ == "__main__": |
| sys.exit(main()) |
|
|