Skip to content

Latest commit

 

History

History
336 lines (255 loc) · 9.8 KB

File metadata and controls

336 lines (255 loc) · 9.8 KB

svg-generator

FastAPI service that generates SVG icons from text prompts using Stable Diffusion 1.5 and converts raster output to clean, optimized SVG.

Model loads lazily — startup is instant. The SD pipeline loads on the first generation request (or POST /wake) and unloads automatically after ICONGEN_IDLE_UNLOAD_SECONDS (default 300 s) of inactivity.

Requirements

  • Linux (Ubuntu 22.04+ recommended)
  • NVIDIA GPU — RTX 3050 8 GB VRAM or better
  • CUDA 11.8+
  • Python 3.10+
  • Node.js 18+

Install

# 1. System packages (Ubuntu/Debian)
sudo apt-get install -y libcairo2-dev libpango1.0-dev libgdk-pixbuf2.0-dev libffi-dev

# 2. Python deps
pip install -r requirements.txt

# 3. Node deps (SVGO)
npm install

# 4. Prefetch model — one time, ~4 GB, no Hugging Face login required
python scripts/download_model.py

# 5. Create output directories
mkdir -p outputs/svg outputs/png outputs/metadata

# 6. Start the service (returns immediately — model loads on first request)
uvicorn app.main:app --host 127.0.0.1 --port 8000

Optional: flat / app-icon style LoRA

Base SD 1.5 is weak at clean icons (it tends to add photo/wood backgrounds). You can load an SD 1.5 style LoRA to get cleaner, more icon-like art. Transparency is still handled in post-processing — the LoRA only improves the source art.

# 1. Prefetch the LoRA (~27 MB, public, no login) into the HF cache
python scripts/download_lora.py

The script prints the env block to enable it. A ready-made svg-generator/.env (gitignored) already contains:

ICONGEN_LORA_REPO=artificialguybr/icons-redmond-1-5v-app-icons-lora-for-sd-liberteredmond-sd-1-5
ICONGEN_LORA_WEIGHT_NAME=IconsRedmond15V-Icons.safetensors
ICONGEN_LORA_SCALE=0.8
ICONGEN_LORA_TRIGGER=icons

Restart the service; the LoRA loads once at startup and icons is prepended to every prompt automatically. To disable, set ICONGEN_LORA_REPO= (empty) or delete .env. Loading is non-fatal: if the LoRA fails to load, the service logs a warning and runs without it. Any SD 1.5 LoRA works — just change the repo/filename/trigger.

Variable Description
ICONGEN_LORA_REPO HF repo id of the LoRA. Empty = disabled.
ICONGEN_LORA_WEIGHT_NAME .safetensors filename inside the repo.
ICONGEN_LORA_SCALE LoRA strength (0.0–1.0, default 0.8).
ICONGEN_LORA_TRIGGER Activation keyword prepended to every prompt.

Endpoints

GET /health

Check service status, GPU availability, and model state.

curl http://127.0.0.1:8000/health
{
  "ok": true,
  "model_loaded": false,
  "idle_seconds": null,
  "active_requests": 0,
  "gpu_available": true,
  "gpu_name": "NVIDIA GeForce RTX 3050"
}

model_loaded is false on a fresh start — it becomes true after the first generation or POST /wake. idle_seconds is null until the model has been loaded at least once.


GET /profiles

List all available style profiles.

curl http://127.0.0.1:8000/profiles
{
  "profiles": {
    "flat-icon": { "name": "flat-icon", ... },
    "filled-icon": { "name": "filled-icon", ... },
    "outline-icon": { "name": "outline-icon", ... }
  }
}

POST /wake

Explicitly load the model now. Use this to pre-warm the service so the first generation call doesn't incur cold-start latency. Returns immediately if the model is already loaded.

curl -X POST http://127.0.0.1:8000/wake
{"ok": true, "model_loaded": true, "load_seconds": 47.3}

load_seconds is 0.0 if the model was already loaded.


POST /sleep

Immediately unload the model and free VRAM. The next generation request will trigger a fresh cold load.

curl -X POST http://127.0.0.1:8000/sleep
{"ok": true, "was_loaded": true}

POST /generate-svg

Generate a single SVG icon. Automatically wakes the model if it is asleep — expect cold-start latency on the first call after idle hibernation.

Request body:

Field Type Required Description
prompt string yes Text description of the icon
name string yes Filename base for saved assets (no path separators)
profile string yes flat-icon | filled-icon | outline-icon
color_palette string[] yes 1–4 hex colors (#rrggbb). Source of truth for SVG fill colors
export_sizes int[] no PNG sizes to export (all ≥ 16). Default: [512, 128, 64, 32, 16]
export_path string no Relative path under the sandbox root. Default: "" (outputs root)
seed int no Seed for reproducibility. Omit for random

Example — flat blue home icon:

curl -X POST http://127.0.0.1:8000/generate-svg \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "home",
    "name": "home-icon",
    "profile": "flat-icon",
    "color_palette": ["#2563eb"],
    "export_sizes": [512, 128, 64, 32, 16],
    "export_path": "my-project/icons"
  }'

Example — two-color outline settings gear:

curl -X POST http://127.0.0.1:8000/generate-svg \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "settings gear",
    "name": "gear",
    "profile": "outline-icon",
    "color_palette": ["#1d4ed8", "#93c5fd"],
    "export_sizes": [512, 64],
    "seed": 42
  }'

Response:

{
  "ok": true,
  "name": "home-icon",
  "svg": "<svg xmlns=\"http://www.w3.org/2000/svg\" ...>...</svg>",
  "svg_path": "/absolute/path/to/outputs/my-project/icons/home-icon.svg",
  "png_paths": [
    "/absolute/path/to/outputs/my-project/icons/home-icon-512.png",
    "..."
  ],
  "metadata": {
    "asset_id": "...",
    "name": "home-icon",
    "seed": 3748291,
    "model": "stable-diffusion-v1-5/stable-diffusion-v1-5",
    "profile": "flat-icon",
    "color_palette": ["#2563eb"],
    ...
  }
}

POST /batch-generate-svg

Generate multiple icons in one request. All items share profile, color_palette, export_sizes, and export_path. Auto-wakes model if asleep.

Request body:

Field Type Required Description
profile string yes Style profile for all items
color_palette string[] yes 1–4 hex colors applied to every icon
items object[] yes Array of {name, prompt, seed?}
export_sizes int[] no Sizes for all icons. Default: [512, 128, 64, 32, 16]
export_path string no Shared output path

Example:

curl -X POST http://127.0.0.1:8000/batch-generate-svg \
  -H "Content-Type: application/json" \
  -d '{
    "profile": "flat-icon",
    "color_palette": ["#2563eb"],
    "export_sizes": [512, 64, 32],
    "export_path": "my-project/icons",
    "items": [
      {"name": "home",     "prompt": "home"},
      {"name": "star",     "prompt": "star"},
      {"name": "gear",     "prompt": "settings gear"},
      {"name": "envelope", "prompt": "envelope"}
    ]
  }'

Response:

{
  "ok": true,
  "results": [ { "ok": true, "name": "home", "svg": "...", ... }, ... ],
  "errors": []
}

Run Tests

python -m pytest tests/ -v

Smoke Test

The service must be running first.

python scripts/test_generate.py

Run as a systemd service

Create /etc/systemd/system/icongen.service:

[Unit]
Description=IconGen SVG Generator Service
After=network.target

[Service]
Type=simple
User=youruser
WorkingDirectory=/absolute/path/to/svg-generator
ExecStart=/absolute/path/to/.venv/bin/uvicorn app.main:app --host 127.0.0.1 --port 8000
Restart=on-failure
RestartSec=5

# Reuse the prefetched model cache — prevents re-download on every boot.
# Set this to wherever `python scripts/download_model.py` stored the weights
# (default: ~/.cache/huggingface/hub).
Environment=HF_HOME=/home/youruser/.cache/huggingface

# Optional overrides:
# Environment=ICONGEN_IDLE_UNLOAD_SECONDS=600
# Environment=ICONGEN_IDLE_UNLOAD_SECONDS=0  ← disable auto-unload
# Environment=ICONGEN_ALLOWED_OUTPUT_ROOT=/var/lib/icongen/outputs

[Install]
WantedBy=multi-user.target

Enable and start:

sudo systemctl daemon-reload
sudo systemctl enable icongen
sudo systemctl start icongen
sudo journalctl -u icongen -f   # tail logs

Note: Startup is near-instant — the model loads on first generation request (or POST /wake), not at boot. The first generation after a cold start or idle hibernation will take 30–120 s for the SD pipeline to load into VRAM.

Environment Variables

All variables use the ICONGEN_ prefix.

Variable Default Description
ICONGEN_SD_MODEL_ID stable-diffusion-v1-5/stable-diffusion-v1-5 Hugging Face model ID
ICONGEN_SD_LOCAL_PATH (empty) Path to a local .safetensors file; skips HF download when set
ICONGEN_ALLOWED_OUTPUT_ROOT ./outputs Sandbox root — all file writes are restricted to this directory
ICONGEN_PROFILES_DIR ./profiles Directory containing style profile JSON files
ICONGEN_MIN_EXPORT_SIZE 16 Minimum allowed PNG export size in pixels
ICONGEN_IDLE_UNLOAD_SECONDS 300 Seconds of inactivity before auto-unloading from VRAM. Set to 0 to keep model resident once loaded.

Notes

  • color_palette is the source of truth for color. Stable Diffusion generates monochrome shapes; colors are applied to SVG paths post-vectorization via palette assignment. Mentioning colors in the prompt may guide composition but does not control the actual SVG fill colors.
  • export_path must be relative. Absolute paths and .. traversal are rejected by both the MCP layer and the API service.
  • seed is returned in metadata so any icon can be reproduced exactly by passing the same seed, prompt, and palette.
  • Cold start latency: The first generation after startup or after idle hibernation takes 30–120 s (SD pipeline loading into VRAM). POST /wake lets you absorb this latency before users arrive.