Glanz-A2-LoRA

Front-end generation for A2, trained on 202 examples of Claude Opus web design from glamour-opus.

Given a design brief, A2 with this adapter writes a complete, self-contained component β€” semantic HTML, a full stylesheet, and any JavaScript it needs β€” instead of the thin sketch the base model produces.

This is the 2-epoch checkpoint, not the final one. See Which checkpoint below; the 4-epoch weights are worse and the 3-epoch weights fail in a specific way.

Measured

22 held-out briefs, never seen in training. Everything below is scored by parsing and executing β€” tags balanced by a real HTML parser, CSS through tinycss2, JavaScript through node --check β€” never by asking a model whether the page looks good.

base A2 + Glanz Opus (ground truth)
structure + balanced, of completed generations 19/20 18/18 22/22
classes with a matching CSS rule 0.776 0.944 0.964
self-contained (no external assets) 21/22 22/22 22/22
CSS rules, mean 10.5 43.0 β€”
output size, mean chars 4,343 14,564 12,911
repetition collapses 2/22 0/22 0/22

The class-coverage number is the one that matters most. A model that has learned the shape of these pages without the substance emits rich markup styled by nothing; at 0.944 against a 0.964 ceiling, the extra markup is genuinely styled. Output length lands on the training distribution rather than overshooting it.

It also fixes a base failure mode. Two of the 22 base generations degenerate into a repeated line β€” <div class="rule"></div> emitted until the token cap, a 0.05 unique-line ratio. The adapter has none, and its worst case is 0.51. This was not a training target; it is a side effect.

Nothing broke

A behavioural probe of the capabilities A2 is actually for β€” tool calling, abstention, identity, Luna persona gating β€” run at temperature 0 before and after:

base A2 + Glanz
behavioural checks passed 7/9 9/9

The two gained cases are the same one: asked "Where is shipment TRK-88213 right now?", base A2 replies "if you provide the tracking number, I can give you details" β€” it will not lift the argument out of the prompt, with or without thinking enabled. With the adapter it calls lookup_shipment(tracking="TRK-88213") correctly.

This is reported as observed, not claimed as a feature. It is two cases, and there is no obvious mechanism by which web-design data teaches argument extraction beyond r=32 touching all seven projections. Do not plan around it.

Tool calling, parallel calls, abstention, the Schneewolf Labs identity under adversarial pressure, and Luna's gating behind her system prompt are all unchanged.

Usage

You must disable thinking. A2's template ends its generation prompt with <think>\n. Training used enable_thinking=False, which makes the template emit a pre-closed <think>\n\n</think>\n\n block instead, so the model was never trained to write HTML from inside an open think block. Generating with thinking on puts it in a state this adapter has never seen.

from peft import PeftModel
from transformers import AutoModelForCausalLM, AutoTokenizer

tok = AutoTokenizer.from_pretrained("schneewolflabs/A2")
model = AutoModelForCausalLM.from_pretrained("schneewolflabs/A2", dtype="bfloat16",
                                             device_map="auto")
model = PeftModel.from_pretrained(model, "schneewolflabs/Glanz-A2-LoRA")

SYSTEM = (
    "You are a front-end designer. Given a design brief, produce a single self-contained web "
    "component: semantic HTML, a complete stylesheet, and any JavaScript it needs. Output three "
    "fenced code blocks in order β€” html, css, js β€” and nothing else. Omit the js block if the "
    "component needs no script. Use no external assets, frameworks, or network requests."
)
enc = tok.apply_chat_template(
    [{"role": "system", "content": SYSTEM},
     {"role": "user", "content": "Design a pricing table for a small SaaS. Dark theme."}],
    add_generation_prompt=True, enable_thinking=False,   # <- required
    return_tensors="pt", return_dict=True).to(model.device)
print(tok.decode(model.generate(**enc, max_new_tokens=16000, do_sample=False)[0]))

A GGUF of the adapter is included for llama.cpp:

llama-server -m A2-Q8_0.gguf --lora-scaled glanz-a2-lora-f16.gguf:1.0 -ngl 99 -c 32768 -fa on

Allow a large token budget. These generations average 14,564 characters. At n_predict=7000, 6 of 22 briefs were cut off mid-page; at 16,000, 4 still were. A truncated page looks exactly like a malformed one β€” the last fence never closes β€” so budget generously or you will measure your own cap.

Which checkpoint

Four epochs were trained and all four evaluated. Held-out loss on 22 rows against loss on an equal-sized slice of training rows:

checkpoint held-out train gap
base 0.7012 0.7065 βˆ’0.005
epoch 1 0.5632 0.5269 0.036
epoch 2 (this one) 0.5339 0.4406 0.093
epoch 3 0.5329 0.3821 0.151
epoch 4 0.5457 0.3686 0.177

Held-out loss bottoms at epoch 3 and rises at epoch 4 while training loss keeps falling β€” the 4-epoch weights are overfit, and they are what a naive save_model at the end of training would have shipped.

Between epochs 2 and 3 the loss difference is 0.001, which is nothing, and the memorisation gap differs by 60%, which is not. The design eval broke the tie decisively: epoch 3 scores 0.966 on class coverage β€” the best of any variant β€” but collapses into repetition on 2 of 22 briefs, the same failure the base model has and epoch 2 eliminates. Buying 0.02 of coverage with two catastrophic loops is a bad trade.

Training

base schneewolflabs/A2 (12B, Mistral-Nemo class)
method SFT, LoRA r=32 Ξ±=64 dropout 0.05
modules q, k, v, o, gate, up, down β€” 114M params (0.92%)
data 202 train / 22 held out, from 224 rows
schedule 4 epochs, 104 steps, lr 1e-4 cosine, warmup 0.05
batch 1 Γ— grad-accum 8
sequence max_length 8192
hardware one RTX A6000, 33 GB peak, 76 minutes
trainer grimoire

Training loss fell 0.762 β†’ 0.357 with no rebound. max_length is 8192 because the longest rendered sample is 6,672 tokens and 4096 would have truncated 180 of 224 β€” teaching the model to stop mid-page would have been worse than not training.

6 of 200 source rows had syntactically invalid JavaScript, truncated mid-token by whatever cap the original Glamour capture ran under. Those blocks were dropped after a node --check pass rather than trained on. The block was dropped rather than the row, because js is the last field generated, so a truncated row's HTML and CSS are complete and still worth learning from.

Limitations

  • Not Opus. Coverage and structure reach the ceiling; the design does not. This model produces well-formed, densely styled, plausible pages, not award-winning ones.
  • Single provenance. 224 briefs from one generator in one aesthetic register. Expect the house style of that corpus.
  • The system prompt matters. Trained under one specific framing; the behaviour does carry to bare prompts (a no-system-prompt design request grew from 435 to 2,996 characters) but the measured numbers are all under the prompt shown above.
  • Scale untested. Everything here is at --lora-scaled 1.0. Lower scales were not swept.
Downloads last month
12
GGUF
Model size
0.1B params
Architecture
llama
Hardware compatibility
Log In to add your hardware

16-bit

Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support

Model tree for schneewolflabs/Glanz-A2-LoRA

Adapter
(1)
this model

Dataset used to train schneewolflabs/Glanz-A2-LoRA