What it produces
A JSON object with exactly these 10 top-level keys:
project, requirements, components, relationships, circuit, fabrication, instructions, appearance, image_generation_prompt, sourcing
. It encodes real design decisions — a pin-level circuit netlist,
phase-ordered build instructions, and a sourcing table whose line items sum to a stated total. The
schema is
schema.json in this repo; a
Product pydantic model + semantic gates (
validate.py) can
re-check any output.
Usage
This adapter ships a chat template that defaults to no-think — the model outputs JSON directly,
no special flags needed. (Qwen3.5's stock template opens a <think> reasoning block by default, which
would make every response fail to parse; the bundled template flips that default. Pass
enable_thinking=True only if you deliberately want reasoning.) Use the same system prompt shape
(10 keys, "output only JSON") the model was trained with.
import torch
from unsloth import FastVisionModel
model, tokenizer = FastVisionModel.from_pretrained("caid-technologies/partipro-base-lora", load_in_4bit=False)
FastVisionModel.for_inference(model)
messages = [
{"role": "system", "content": [{"type": "text", "text": SYSTEM_PROMPT}]},
{"role": "user", "content": [{"type": "text", "text": "Design a USB-powered desk lamp with touch dimming."}]},
]
text = tokenizer.apply_chat_template(messages, add_generation_prompt=True, tokenize=False)
inputs = tokenizer(text=text, return_tensors="pt").to("cuda")
out = model.generate(**inputs, max_new_tokens=13000, do_sample=False, repetition_penalty=1.1)
print(tokenizer.decode(out[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True))
For production serving, use schema-constrained decoding. Under vLLM, pass schema.json as a
guided_json constraint — this lifts schema-validity dramatically (see Evaluation) and makes malformed
JSON structurally impossible rather than merely unlikely. Note that when serving base + this adapter
through vLLM, the engine applies the base chat template, which defaults to thinking mode — pass
chat_template_kwargs={"enable_thinking": false} (or serve the merged model, which bundles the no-think
template). Then re-validate with validate.py and retry on gate failures.
Evaluation
Measured with vLLM on 60 held-out in-distribution examples (15 products the model never trained
on × 4 input variants: prompt only / +brief / +sketch / +brief+render), a 42-prompt out-of-distribution
suite, and a sampling-robustness matrix. Strict parse = raw json.loads, no salvage (the deployment
bar). Every output scored against the real schema (schema.py) and semantic gates (validate.py). The
eval ran base + this LoRA; because the vision tower is frozen, these numbers also describe the merged model.
In-distribution (60 held-out)
Table with columns: Metric, Fine-tuned — free, Fine-tuned — guided_json, Base Qwen3.5-9B| Metric | Fine-tuned — free | Fine-tuned — guided_json | Base Qwen3.5-9B |
|---|
| Strict JSON parse | 0.85 | 0.83 | 0.97 |
Schema-valid (Product) | 0.67 | 0.83 | 0.00 |
| Gates-clean (pins / sourcing math / phase order) | 0.40 | 0.48 | 0.00 |
| Prompt-echo fidelity |
The base model emits parseable JSON (0.97) but it is never a valid blueprint (schema 0.00) — right
braces, wrong content. The fine-tune produces a schema-valid blueprint 67% of the time under free
decoding and 83% under schema-constrained decoding. Quality scales with context: the strongest
variant (prompt + brief + render image) reaches 0.73 schema-valid / 0.93 echo under free decoding.
Out-of-distribution (42 fresh prompts)
Table with columns: kind, n, parse, schema (free)| kind | n | parse | schema (free) |
|---|
| realistic paraphrase | 10 | 0.90 | 0.70 |
| new domain | 10 | 0.90 | 0.70 |
| vague / underspecified | 5 | 0.80 | 0.40 |
| image-grounded | 6 | 0.83 | 0.50 |
| adversarial |
Across the realistic OOD rows (paraphrase + new-domain + vague + image), schema-valid is 0.61 under
free decoding and 0.97 under guided_json — constrained decoding is the difference between a demo and
a deployable endpoint on unseen inputs. Adversarial rows (impossible physics, contradictory constraints,
prompt-injection) are the weakest, as intended: the model tries to comply and sometimes breaks.
Sampling robustness
At temperature=0.7 (30 generations, 3 seeds): strict parse holds at 0.97, schema-valid 0.57 —
sampling does not break the JSON structure.
Reading these numbers for deployment
- Serve with
guided_json. Passing the blueprint schema as a vLLM structured-output constraint lifts
schema-valid from 0.61 → 0.97 on unseen prompts (and 0.67 → 0.83 in-distribution). This is the
intended serving path, not an optional extra.
- Parse failures are truncation, not malformed output. 10 of 126 free generations hit the 13,000-token
cap; there were 0 thinking-leaks, 0 markdown fences, 1 trailing-data. Raising the token budget and/or
repetition_penalty (≥1.1) recovers most of these and lifts parse toward ~0.95.
- Gates-clean (~0.40–0.48) is the honest quality ceiling. Roughly half of outputs have at least one
semantic issue a human wouldn't catch at a glance (a pin on the wrong net, sourcing totals off, a phase
mis-ordered). Constrained decoding does not fix these. Always re-run outputs through
validate.py
and regenerate on failure — this is the intended workflow.
Training data
150 synthetic records, each validated by the same schema + semantic gates before inclusion
(pin-checked circuits, sourcing arithmetic, phase-ordered instructions). Records were authored by
Claude Sonnet 5 and DeepSeek-V4-Pro; concept images (render + hand-drawn sketch per record) by
gpt-image-1. Four input variants per record (prompt only; prompt + brief; prompt + sketch;
prompt + brief + render) teach the model to read documents and images, not just bare prompts.
- LoRA: r=16, α=16, bf16, language+attention+MLP layers (vision encoder frozen), 3 epochs, max seq 16384.
- Base model: Qwen/Qwen3.5-9B (Apache-2.0).
Limitations
- English prompts only; maker/electronics/hardware domain. Off-task prompts still tend to produce a
blueprint (parse 1.00, schema 0.75 on the off-task suite) — it does not reliably refuse.
- Outputs are plausible engineering, not verified builds — the gates check internal consistency
(valid pins, correct arithmetic, ordered phases), not real-world electrical safety or buildability.
Review before you solder.
- ~half of outputs have a semantic gate issue (see Evaluation) — treat every output as a draft to
re-validate with
validate.py, not a finished spec.
- Long outputs can truncate at the token cap (10/126 in eval); use
repetition_penalty≥1.1 and a
generous max_new_tokens (≥13k), or constrained decoding.
- Synthetic training data inherits the generators' biases. Adversarial/contradictory requests
(impossible physics, negative budgets) may yield confidently wrong blueprints (parse 0.43 on the
adversarial suite).
- LoRA adapter (~165 MB); requires
Qwen/Qwen3.5-9B as the base (auto-downloaded on load) and is tied
to that model at this revision. It defaults to no-think via a patched chat template; pass
enable_thinking=True only if you want reasoning.
License
Apache-2.0, inheriting the base model's license. The adapter weights are released under the same terms.