Downloads · 30 days
0
John-Williams-ATL/svg-chart-lora
svg-chart-lora is a text generation model from John-Williams-ATL. Use it when you need the model to write or continue text. It is set up for mlx. The card lists the license as apache-2.0.
12 per-type LoRA adapters for SVG business chart generation, fine-tuned on Gemma 3 12B Built and published by John Williams / fxops.ai
Downloads · 30 days
0
Access
Public
Updated Apr 3, 2026
Repo size
1 GB
Likes
0
Public
Click a slice to open those files.
.safetensors873 MB · 100%
From the Hugging Face model README
12 per-type LoRA adapters for SVG business chart generation, fine-tuned on Gemma 3 12B
Built and published by John Williams / fxops.ai
A set of 12 specialist LoRA adapters — one per chart type — that turn Gemma 3 12B into a deterministic SVG chart generator. Given structured JSON input (headings, labels, values), the model produces a valid, renderable SVG chart with correct coordinate geometry, proportional data representation, and consistent visual style.
The entire stack runs on-device. No API calls at inference time. Trained and deployed on an Apple M4 Mac mini with 24GB unified memory.
Chart types: bar · line · area · pie · donut · funnel · scatter · bubble · grouped_bar · stacked_bar · waterfall · horizontal_bar
from mlx_lm import load, generate
from mlx_lm.sample_utils import make_sampler
# Load base model + chart-type adapter
model, tokenizer = load(
"mlx-community/gemma-3-12b-it-4bit",
adapter_path="John-Williams-ATL/svg-chart-lora/adapters_funnel"
)
# Build prompt (see per_type_system_prompts.py for full system prompt)
messages = [
{"role": "system", "content": "You are a funnel chart specialist..."},
{"role": "user", "content": """Chart type: funnel
Heading: Sales Pipeline
Pre-computed bars (centered at x=250, use coordinates exactly):
'Leads' (1000): x=50.0 y=40 width=400.0 height=44 label at x=250 y=66
'Qualified' (600): x=110.0 y=90 width=280.0 height=44 label at x=250 y=116
'Proposal' (300): x=170.0 y=140 width=160.0 height=44 label at x=250 y=166
'Closed' (120): x=202.0 y=190 width=96.0 height=44 label at x=250 y=216
Return ONLY the SVG code."""}
]
prompt = tokenizer.apply_chat_template(
messages, tokenize=False, add_generation_prompt=True
)
sampler = make_sampler(temp=0.1, top_p=0.95)
svg_output = generate(model, tokenizer, prompt=prompt,
max_tokens=2000, sampler=sampler, verbose=False)
print(svg_output) # Valid SVG — write to file or pipe to renderer
Switch chart types by changing the adapter path and system prompt:
# Bar chart
model, tokenizer = load("mlx-community/gemma-3-12b-it-4bit",
adapter_path="John-Williams-ATL/svg-chart-lora/adapters_bar")
# Line chart
model, tokenizer = load("mlx-community/gemma-3-12b-it-4bit",
adapter_path="John-Williams-ATL/svg-chart-lora/adapters_line")
Full inference pipeline with geometry pre-computation and 4-stage validation:
→ See gemma3_svg_chart_generation_v2_1.ipynb in this repository.
Evaluated across 20 runs per type at temp=0.1 using an automated scoring harness.
Pre-training baseline included for retrained types (marked *).
| Type | Pass Rate | Before* | Delta | Status |
|---|---|---|---|---|
| area | 100% | 100% | = | ✅ Production |
| bubble | 100% | 100% | = | ✅ Production |
| donut | 100% | 100% | = | ✅ Production |
| funnel | 100% | 100% | = | ✅ Production |
| grouped_bar | 100% | 100% | = | ✅ Production |
| line * | 100% | 0% | +100% | ✅ Production |
| stacked_bar * | 95% | 65% | +30% | ✅ Near production |
| horizontal_bar | 90% | 100% | -10% | ✅ Near production |
| pie | 80% | 90% | -10% | ✅ Near production |
| bar * | 0% | 0% | = | 🔄 v2.0 target |
| scatter * | 0% | 0% | = | 🔄 v2.0 target |
| waterfall * | 0% | 0% | = | 🔄 v2.0 target |
* Retrained in v1.0. Line adapter is the standout result — 0%→100% in one training pass.
Each chart type has distinct failure modes. A single multi-type adapter spreads training signal across 12 geometrically different patterns. Per-type adapters focus each adapter's capacity on one layout — coordinate arithmetic, element ordering, label placement — specific to that chart.
The inference pipeline pre-computes all coordinate arithmetic in Python before sending to the model. For complex types (donut, pie, scatter, bubble, waterfall), the prompt includes exact SVG path strings, circle positions, and bar coordinates. The model's job is SVG assembly, not trigonometry.
This separates concerns cleanly:
Every generated SVG passes through GemmaChartValidator before use:
| Gate | Check |
|---|---|
| Gate 0 | Guard against empty output |
| Gate 1 | SVG extraction — strip think blocks, markdown fences |
| Gate 2 | XML parse validation |
| Gate 3 | Content rules — viewBox, coordinate bounds, no expressions |
| Gate 4 | Text overflow repair (x-clamp) |
| Gate 4b | Rect coordinate repair (bar baseline) |
| Gate 4c | Axis line stroke injection |
| Parameter | Value |
|---|---|
| Base model | google/gemma-3-12b-it (text variant) |
| Quantisation | BF16 training / 4-bit inference |
| Framework | MLX + mlx-lm |
| Hardware | Apple M4 Mac mini 24GB |
| Adapter type | LoRA, 16 layers |
| Iterations | 300 per type |
| Sequence length | 4096 tokens |
| Training examples | ~156 per type (v1.0, merged original + retrained) |
| Dataset | John-Williams-ATL/svg-chart-training-data |
| Temperature | 0.1 (deterministic at this setting) |
Training data generated using Claude (Anthropic) for initial examples, then migrated to local gemma3:12b via Ollama for scale, and finally to MLX direct inference for the v1.0 retraining pass.
All 12 adapters share a fixed output canvas:
viewBox="0 0 500 300" width="500" height="300"
Background : #f5f5f5
Palette : #4a90d9 · #e67e22 · #27ae60 · #8e44ad
Text color : #333333
Font : sans-serif
Origin : top-left (0,0). x rightward, y downward.
Chart area : x=50..480, y=30..270
Baseline : y=270 (vertical bar and waterfall charts)
adapters_area/
adapters_bar/
adapters_bubble/
adapters_donut/
adapters_funnel/
adapters_grouped_bar/
adapters_horizontal_bar/
adapters_line/
adapters_pie/
adapters_scatter/
adapters_stacked_bar/
adapters_waterfall/
adapter_config.json ← LoRA config (rank, alpha, base model path)
adapters.safetensors ← trained adapter weights
scripts/
per_type_system_prompts.py ← system prompts for all 12 types
generate_training_data.py ← dataset generation pipeline (MLX backend)
chart_registry.py ← chart type registry
run_per_type_training.sh ← MLX LoRA training runner
eval_harness.py ← automated 20-run evaluation harness
gemma3_svg_chart_generation_v2_1.ipynb ← demo notebook
dy coordinates and a Gemma 4 spatial criticv2.0 — in development, targeting Gemma 4:
dy coordinates replacing absolute running totalsJohn Williams — AI transformation practitioner and independent operator.
fxops.ai · HuggingFace · X @fxopsAI
Built in public as part of the fxops.ai private AI infrastructure stack. Training methodology, architecture decisions, and operational history documented in Substack series: Teaching a Small Model to Do One Thing Well.
Apache 2.0. Base model (Gemma 3) is subject to Google's Gemma Terms of Use.