Qwen3-1.7B Frontend SFT

A QLoRA supervised fine-tune of Qwen/Qwen3-1.7B for front-end code generation: complete HTML documents, CSS, JavaScript, and whole-file edits.

The repository root holds the merged 16-bit model (loads directly, no PEFT needed). The LoRA adapter alone is under adapter/.

Usage

from transformers import AutoModelForCausalLM, AutoTokenizer

model_id = "panzarasa/qwen3-1.7b-frontend-sft"
tok = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(model_id, torch_dtype="bfloat16", device_map="auto")

messages = [
    {"role": "system", "content": "You are an expert front-end engineer. Given a website description, reply with a single complete HTML document with its CSS in a <style> block."},
    {"role": "user", "content": "A landing page for a coffee roastery: hero, three product cards, newsletter form, footer."},
]
ids = tok.apply_chat_template(messages, add_generation_prompt=True, return_tensors="pt").to(model.device)
out = model.generate(ids, max_new_tokens=8192, temperature=0.7, top_p=0.9, repetition_penalty=1.05)
print(tok.decode(out[0][ids.shape[-1]:], skip_special_tokens=True))

Sampling settings

temperature=0.7, top_p=0.9, repetition_penalty=1.05, max_new_tokens=8192

These are a reasonable default, not a fix for the failure mode below. Both 0.3 and 0.7 were measured on the same six held-out pages and scored the same; the rows that fail differ, the count does not.

Known failure mode: long pages may not terminate

On long full-page generation (the webcode2m style: a real-world page with a trailing <style> block), the model can fall into a repetition loop and burn the whole token budget without emitting </html>. Two shapes were observed:

  • textual -- a sentence or a counter repeats (vitamin B580, B581, ...);
  • structural -- markup cycles (one generated page held 518 <li> elements drawn from 8 distinct variants).

Measured on 6 random held-out webcode2m rows, at both temperatures: 4 of 6 reached </html>; 2 of 6 hit the 8192-token cap. Raising the temperature does not reliably help. Detect it rather than prevent it: check that the output ends in </html> and retry if it does not.

Short outputs -- single components, JavaScript functions, whole-file edits, the websight style of compact Tailwind page -- did not show this in testing.

Prompt format

The model was trained with the Qwen/Qwen3-4B-Instruct-2507 chat template, not the hybrid-thinking template that ships with Qwen3-1.7B. The tokenizer in this repo already carries the right one. There are no <think> blocks in the output.

System prompts seen during training:

  • You are an expert front-end engineer. Given a website description, reply with a single complete HTML document ...
  • You are an expert front-end engineer. Given a page brief, reply with a single complete HTML document with its CSS in a <style> block.
  • You are a senior front-end engineer. Answer with correct, production-ready HTML, CSS and JavaScript. Be concise.
  • You are an expert front-end engineer. Apply the requested change to the file and output only the complete updated file. No explanation, no markdown fences.

Training

Base Qwen/Qwen3-1.7B (loaded 4-bit nf4 via unsloth/Qwen3-1.7B-unsloth-bnb-4bit)
Method QLoRA, r=32, alpha=32, dropout 0
Target modules q, k, v, o, gate, up, down
Trainable params 34,865,152 (2.03% of 1.72B)
Sequence length 8,192 (prompt + answer)
Epochs 1 (16,701 steps)
Effective batch 16 (2 x grad-accum 2 x 4 GPUs)
LR 2e-4, cosine decay
Loss on assistant tokens only
Hardware 4x RTX 4090 24GB, 4h 33m
Final eval loss 0.3713

Rows longer than 8,192 tokens were dropped, not truncated, so the model never saw an unterminated document.

Data

267,202 training rows / 1,981 validation rows, mixed from:

Source Rows Mean tokens What it teaches
bigcode/commitpackft 61,312 475 whole-file edits from a change request
HuggingFaceM4/WebSight 60,000 557 description to Tailwind page
xcodemind/webcode2m 59,998 4,544 long real-world pages with a <style> block
Tesslate/Next.js-Dataset 49,954 1,601 Next.js / React answers with explanation
saurabh5/rlvr-code-data-JavaScript-sft 40,000 363 JavaScript exercises
glyphsoftware/opus-4.6-frontend-development 715 1,297 front-end conversations
runanlab/gpt-5.4-frontend-development-27052026 665 6,551 long front-end builds

Each source keeps its own licence; check them before redistributing derived data.

Evaluation

Eval loss decreased monotonically over the single epoch and flattened at the end:

epoch eval loss
12% 0.4757
24% 0.4384
36% 0.4168
48% 0.3983
60% 0.3886
72% 0.3769
84% 0.3711
100% 0.3713

Held-out generation

One held-out row per source was generated and read by hand:

Source Result
websight valid Tailwind page, closed, semantic header/main/footer
saurabh_js correct lucas(n) with base cases and input validation, code only
Tesslate valid Next.js App Router POST handler, 400/500 paths, matches the source's prose register
commitpackft whole updated file returned, 11/11 lines, no diff, no fences, requested attributes added
webcode2m see the failure mode above

Six further random webcode2m rows were generated at two temperatures and scored on closure, <style> presence, tag balance and repeated blocks. Model: 2/6 at either temperature. The reference pages score 3/6 on the same metric -- real scraped pages are themselves repetitive, so the gap is narrower than the raw number suggests.

Training data duplication

xcodemind/webcode2m is a web scrape, and the same site template recurs many times. Hashing each page's tag sequence (structure only, ignoring text and attributes) across the 59,566 training rows:

  • 40,373 distinct structures; 32.2% of the rows are structural duplicates
  • 419 structures appear 10+ times; the largest cluster holds 171 copies

There is no exact train/val leakage (0 identical documents), but 52 of the 390 distinct validation page titles also occur in training. The effect is visible in generation: on two of the six sampled rows the model reproduces the held-out reference at 99.6% and 98.1% similarity, including a 2,091-char verbatim span. Temperature does not suppress it.

Practical consequence: eval loss understates the true generalization gap on long pages, and output for this kind of page may closely resemble a real website. Deduplicating by structural hash before the split would remove 19,000 rows (20% of all training tokens) and is the first change worth making.

Limitations

  • Long full-page generation fails to terminate roughly 1 time in 3; check for </html> and retry.
  • Can reproduce training pages near-verbatim on the webcode2m style -- see Training data duplication. Do not assume a generated page is original.
  • Can skip elements a brief explicitly asks for (a <form> was missed in one held-out page that requested one).
  • Trained for one epoch; eval loss had flattened under a cosine schedule decayed to 1e-6. More epochs on this data would deepen the duplication problem, not fix it -- deduplicate first.
  • English only.
  • 1.7B parameters: expect scaffolding and structure, not production copy.
Downloads last month
424
Safetensors
Model size
2B params
Tensor type
BF16
·
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for panzarasa/qwen3-1.7b-frontend-sft

Finetuned
Qwen/Qwen3-1.7B
Finetuned
(1245)
this model

Datasets used to train panzarasa/qwen3-1.7b-frontend-sft