"""Four arms, one prompt, one set of beats — harness-matched on the live seat. THE PROMPT IS DELIBERATELY AUTHOR-NEUTRAL. Each adapter was trained under a system prompt naming its own author ("in the manner of Charlotte Brontë — mid-nineteenth-century first-person retrospective ..."). Driving all four with any ONE of those would hand that author's arm a hint the others do not get, and the comparison would measure the prompt. So the author clause is removed and the shared task skeleton kept. What is left varies only by which LoRA is loaded. ⚠ ONE DISCLOSED ASYMMETRY: Brontë and Hemingway trained on "a SHORT PASSAGE ... may run to several paragraphs"; Yarros trained on "ONE paragraph". The neutral prompt uses neither qualifier, so it sits slightly off-distribution for all three rather than for one. Sampler matches the gate harness exactly: temperature 0.9, top_p 0.95, "BEAT: " prefix. Two seeds per cell, because one sample of a sampled process is an anecdote — the second is rendered behind a toggle so the page stays readable but the variance is one click away. """ import json, urllib.request, sys, time SEAT = "http://10.251.50.54:8027/v1/chat/completions" ARMS = [("voices-base", "control"), ("lv-bronte", "Brontë"), ("lv-yarros", "Yarros"), ("lv-hemingway", "Hemingway")] SEEDS = [1234, 5678] SYS = ("You expand a single story beat into a SHORT PASSAGE of prose. Render the beat " "itself; do not move past it, do not begin a new scene, do not comment, do not " "write a chapter heading. Output the prose only, 90–140 words.") BEATS = [ ("b1", "She tells him she is leaving in the morning, and he does not ask her to stay."), ("b2", "He refuses the money a second time, and the other man sets it down on the table anyway."), ("b3", "She finds him bleeding on the stairs and asks how long he has been sitting there."), ("b4", "He waits past the hour they agreed, orders another drink, and watches the door."), ("b5", "She reads the letter twice, then puts it in the fire without saying what it said."), ("b6", "He tells her the truth about the accident, and she says nothing for a long time."), ] def gen(model, beat, seed): body = {"model": model, "seed": seed, "temperature": 0.9, "top_p": 0.95, "max_tokens": 320, "messages": [{"role": "system", "content": SYS}, {"role": "user", "content": "BEAT: " + beat}]} req = urllib.request.Request(SEAT, data=json.dumps(body).encode(), headers={"Content-Type": "application/json"}) for attempt in range(3): try: r = json.load(urllib.request.urlopen(req, timeout=180)) return r["choices"][0]["message"]["content"], r.get("model") except Exception as e: if attempt == 2: raise print(f" retry {attempt+1} after {e}", file=sys.stderr) time.sleep(5) out = {"system": SYS, "beats": BEATS, "arms": ARMS, "seeds": SEEDS, "cells": {}} t0 = time.time() for model, label in ARMS: for bid, beat in BEATS: for seed in SEEDS: txt, served = gen(model, beat, seed) out["cells"][f"{model}|{bid}|{seed}"] = {"text": txt, "served": served} print(f" {model:<14} {bid} seed={seed} {len(txt.split()):>4}w " f"served={served}", flush=True) print(f"done in {time.time()-t0:.0f}s") json.dump(out, open(sys.argv[1], "w"), ensure_ascii=False, indent=1)