Salvage the SD lane: patch MODELBEAST, add the SD backend, fix the assets filter at source

The LoRA bench concluded the SD1.5 lane was invalid for garments. That was true of the
CHECKPOINT, not the lane: Hyper_Realism_1.2 was the only one the farm could reach and it is a
person-photoreal merge that framed every garment as worn-and-cropped no matter how hard you
negative-prompted. Given four non-person checkpoints, the lane works — and now beats flux.

MODELBEAST patches (John's explicit blessing; flux_local deliberately untouched because a
parallel session owned it and had already deployed LoRA + Z-Image support there):
· comfyui_sd now STACKS LoRAs. It hard-coded a single LoraLoader, so style+texture together
  was impossible; `lora` now takes a list, each entry optionally `name=0.6`, chained.
· comfyui_sd gains ControlNet (loader + ApplyAdvanced + staged control image). The payoff for
  a wardrobe is recolouring one garment into N colourways that share a byte-identical
  silhouette, so a single cut-out alpha is reusable across every variant.
· Per-node preflight widened to LoRAs and ControlNets, and the arch-mismatch warning now fires
  per LoRA. A wrong-base LoRA is a SILENT no-op, so everything fails loudly instead.
· Deployed to all three nodes that run comfyui_sd (m3ultra, ultra=m1, m4pro=m4), each backed up.
· GET /api/assets now honours ?parent_job=. It declared no such parameter, so FastAPI silently
  dropped it and every caller got the whole table — 2,578 rows for a bogus id. Callers that
  took rows[0] were right only by accident of newest-first ordering. Verified: bogus -> 0 rows,
  real -> only its own. Restarted the queue in a zero-active-jobs window.
· Copied 4 non-person checkpoints ultra -> m3ultra over the fast LAN (12.6 GB).

wardrobegod:
· `sd` backend on /api/gen, defaulting to juggernautXL_ragnarok, with checkpoint/lora/negative
  passthrough. Measured on one prompt+seed: a full isolated flat-lay where Hyper_Realism crops
  a worn garment.
· flat3d template gains 'no hanger, no coat hanger' — a wire hanger appeared in most SD product
  shots and keys into the cutout as if it were garment. doll2d already had the clause.

Verified end-to-end: 7/7 salvage cells ran (including a 2-LoRA stack, previously impossible),
and a Coogi knit generated through wardrobegod's own /api/gen came back isolated on white with
legible raised-knit texture.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
type-two 2026-07-24 18:14:21 +10:00
parent e3d1a0aa13
commit 5c80e65d58
2 changed files with 34 additions and 2 deletions

View File

@ -116,9 +116,11 @@ def doll_catalogue():
# keying cleanly (7/7 flood seeds) on both a dark solid and a busy multicolour knit, so
# white is the default and green is the escape hatch, not the other way round.
TEMPLATES = {
# 'no hanger, no coat hanger' is not padding: without it a wire hanger showed up in most
# SD product shots (and some flux ones), and a hanger keys into the cutout as garment.
'flat3d': ('product photo of a single {phrase} laid flat on a plain white background, front view, '
'no mannequin, no person, no text, soft even lighting, 1990s Australian op-shop garment, '
'slightly worn'),
'no mannequin, no person, no hanger, no coat hanger, no text, soft even lighting, '
'1990s Australian op-shop garment, slightly worn'),
'doll2d': ('paper doll clothing item for a dress-up game, {phrase}, {year} australia, {view}, '
'floating centered on a plain pure white background, painterly stylized 3d game '
'illustration, full item visible, no body, no person, no mannequin, no hanger, '
@ -133,6 +135,15 @@ DOLL_PROMPT = TEMPLATES['doll2d_green'] # back-compat alias
FLAT_PROMPT = TEMPLATES['flat3d']
# The SD lane's default. Hyper_Realism was the only checkpoint the farm could reach and it is a
# person-photoreal merge — it framed every garment as worn-and-cropped no matter how hard you
# negative-prompted. juggernautXL is a product/material specialist; measured on the same
# prompt+seed it returns a clean isolated flat-lay.
SD_CKPT = os.environ.get('WG_SD_CKPT', 'juggernautXL_ragnarok.safetensors')
SD_NEG = ('person, human, body, model, mannequin, face, hands, legs, worn by someone, '
'hanger, coat hanger, (worst quality, low quality:1.4), blurry, watermark, text')
def gen_seed(phrase, variant=0):
"""Reproducible seed from the phrase — reroll by bumping `variant`.
@ -596,6 +607,26 @@ class H(BaseHTTPRequestHandler):
with open(tgt, 'wb') as f:
f.write(base64.b64decode(out['result']['image']))
return tgt
if backend == 'sd':
# SDXL via ComfyUI. juggernautXL is a product/material specialist and beats
# the SD1.5 default outright: measured side by side on the same prompt+seed it
# gives a full isolated flat-lay, where Hyper_Realism frames the garment as
# worn and crops it at the edge. `lora` may be a list — the operator chains them.
JOBS[jid]['note'] = 'comfyui_sd ' + body.get('checkpoint', SD_CKPT)
params = {'prompt': style, 'negative': body.get('negative', SD_NEG),
'checkpoint': body.get('checkpoint', SD_CKPT),
'steps': int(body.get('steps') or 25), 'cfg': float(body.get('cfg', 7.0)),
'width': wide, 'height': wide, 'seed': seed}
if body.get('lora'):
params['lora'] = body['lora']
params['lora_weight'] = body.get('lora_weight', 0.8)
j = mb_req('/api/jobs', {'operator': 'comfyui_sd', 'params': params})
gid = j.get('id') or j.get('job_id')
mb_wait(gid, jid, timeout=900)
outs = mb_outputs(gid)
if not outs:
raise RuntimeError('no image came back')
return mb_download(outs[0], tgt)
JOBS[jid]['note'] = 'flux_local klein-4b'
j = mb_req('/api/jobs', {'operator': 'flux_local',
'params': {'prompt': style, 'model': 'flux2-klein-4b',

View File

@ -177,6 +177,7 @@
<option value="doll2d_green">doll2d_green — plaid/busy</option></select></div>
<div class="row"><label>backend</label>
<select id="genBackend"><option value="klein">klein-4b — sharper fabric</option>
<option value="sd">juggernautXL — product/material</option>
<option value="cf">cf schnell — 2s, softer</option></select></div>
<div class="row"><label>variant</label><input id="genVariant" value="0" style="width:56px">
<button id="genReroll" class="ghost">↻ reroll</button></div>