"""Render the base-vs-tuned voice A/B into a booth page. Layout is the argument. A flat gallery would let you read one arm at a time, which is exactly how you talk yourself into seeing a difference. So every prompt is one row, the arms are side by side, and BOTH seeds of each arm sit in the same cell -- so the within-arm variation is visible in the same glance as the between-arm variation. If the two base samples differ from each other as much as base differs from tuned, there is nothing here, and the layout should make that obvious rather than hide it. Prompts are ordered by tier, hardest first: modern/mundane, then period-neutral, then Victorian-adjacent. The modern tier is the one that matters -- Brontë showing up there is the adapter's doing, whereas Brontë showing up in the period tier could just be the prompt. """ import html import json import sys from collections import defaultdict from pathlib import Path base_f, tuned_f, out_dir = sys.argv[1], sys.argv[2], Path(sys.argv[3]) out_dir.mkdir(parents=True, exist_ok=True) def load(p): d = defaultdict(dict) for line in Path(p).read_text(encoding="utf-8").splitlines(): if not line.strip(): continue r = json.loads(line) d[r["id"]][r["seed"]] = r return d base, tuned = load(base_f), load(tuned_f) ids = [i for i in base if i in tuned] TIER = {"modern": ("Tier A — modern / mundane", "Nothing here invites Victorian prose. Brontë in this tier is the adapter's doing."), "neutral": ("Tier B — period-neutral", "Could be any century. A voice shift shows cleanly without the prompt supplying it."), "period": ("Tier C — Victorian-adjacent, plainly worded", "The setting leans period but the diction does not. Easiest tier; weakest evidence.")} order = {"modern": 0, "neutral": 1, "period": 2} ids.sort(key=lambda i: (order.get(base[i][list(base[i])[0]]["tier"], 9), i)) def cell(rec_by_seed): parts = [] for seed in sorted(rec_by_seed): t = (rec_by_seed[seed]["continuation"] or "").strip() parts.append(f'
{html.escape(t) or "(empty)"}
{html.escape(sub)}
') rows.append(f"""Same prompts, same sampler, same box, same seeds. The only difference between the columns is the H02 LoRA adapter (Charlotte Brontë, 680k words, 1 epoch, seed 4919).