CLI tool for automated character LoRA training using cloud providers.
Training images are generated with Runware (Flux.1 Kontext). LoRA training runs on RunPod (Flux.2 Klein 4B — primary, ~1.5 it/s on RTX PRO 4500), Replicate, or Fal.ai (both Flux.1-dev only). Weights are uploaded to Runware for inference.
Character LoRA can be stacked with a style LoRA at inference:
character LoRA (~0.70) + watercolor style LoRA (~0.85) → consistent illustrated character
- Python 3.11+
- uv
- API keys for inference (Runware), training (RunPod recommended, or Replicate/Fal.ai for Flux.1-dev), and S3-compatible storage (required for RunPod; recommended for Replicate)
# 1. Install
uv sync
# 2. Configure
cp .env.example .env
# Fill in your API keys (see Environment Variables below)
# 3. Verify
uv run loracraft --help| Variable | Required | Description |
|---|---|---|
TRAINING_PROVIDER |
No | runpod (default) or replicate / fal (Flux.1-dev only) |
CHARACTER_LORA_WEIGHT |
No | Inference weight for character LoRA. Default: 0.70 |
STYLE_LORA_WEIGHT |
No | Inference weight for style LoRA. Default: 0.85 |
RUNWARE_API_KEY |
Yes | Image generation + inference. runware.ai |
RUNWARE_FLUX_MODEL_ID |
Yes | AIR for base model on Runware. Default: runware:101@1 |
RUNWARE_KONTEXT_MODEL_ID |
Yes | AIR for Flux.1 Kontext on Runware. Default: runware:106@1 |
RUNWARE_MODEL_SOURCE |
Yes | Source identifier for model uploads. Create at app.runware.ai → Models → Sources. |
RUNPOD_API_KEY |
If using RunPod | runpod.io/console/user/settings — needs Pod Read + Write permissions |
RUNPOD_HF_TOKEN |
If using RunPod | HuggingFace token with read access — required, Klein base model is gated |
RUNPOD_GPU_TYPE_ID |
No | RunPod GPU type. Default: NVIDIA RTX PRO 4500 Blackwell |
RUNPOD_BASE_MODEL |
No | HF repo for base model. Default: black-forest-labs/FLUX.2-klein-base-4B |
RUNPOD_CLOUD_TYPE |
No | SECURE or COMMUNITY. Default: SECURE |
RUNPOD_CONTAINER_DISK_GB |
No | Container disk size in GB. Default: 50 |
REPLICATE_API_KEY |
If using Replicate | replicate.com/account/api-tokens |
REPLICATE_MODEL_OWNER |
If using Replicate | Your Replicate username |
FAL_API_KEY |
If using Fal.ai | fal.ai/dashboard/keys |
S3_ENDPOINT_URL |
Required for RunPod | S3-compatible storage endpoint (Cloudflare R2 recommended — no egress fees) |
S3_ACCESS_KEY_ID |
Required for RunPod | S3 access key |
S3_SECRET_ACCESS_KEY |
Required for RunPod | S3 secret key |
S3_BUCKET_NAME |
Required for RunPod | S3 bucket name |
S3_REGION |
No | Bucket region. Default: auto |
Runs all steps end-to-end with manual review gates at each stage.
# Interactive prompts
uv run loracraft create-character
# From a config file
uv run loracraft create-character --config characters/luna.yaml
# Options
uv run loracraft create-character \
--config characters/luna.yaml \
--provider replicate \
--steps 1000 \
--rank 16 \
--test-scenes 3 \
--no-runware-uploadPipeline steps:
1. Generate reference image (1024×1536 portrait, vanilla FLUX.1-dev)
↓
[REVIEW] Approve character design / retry / quit
↓
2. Generate 20 training images via Flux.1 Kontext (reference-anchored)
↓
[REVIEW] Approve training set / retry / [s]elect slots to regenerate / edit manually / quit
↓
3. Submit LoRA training job (RunPod ~20–25 min · Replicate ~20–30 min · Fal ~5–15 min · ~$1–2/run)
↓
4. Download weights → characters/<name>/lora/<name>.safetensors
5. Upload to Runware (unless --no-runware-upload)
↓
[REVIEW] Review test scenes / retry / approve
↓
6. Register in characters/registry.json
Generate a reference image only — without running training.
uv run loracraft generate-reference --config characters/luna.yamlSubmit a training job against a pre-curated images directory.
uv run loracraft train luna \
--images-dir characters/luna/training \
--provider replicate \
--steps 1000 \
--rank 16# Single poll
uv run loracraft training-status luna
# Watch until complete
uv run loracraft training-status luna --watch --interval 30Re-upload existing local weights without re-training.
uv run loracraft upload-lora lunauv run loracraft list-charactersGenerate scenes with a trained character LoRA. Optionally stack a style LoRA.
uv run loracraft test-character luna --scenes 4
# With a style LoRA (Runware AIR)
uv run loracraft test-character luna \
--scenes 4 \
--style-lora civitai:512147@1271855Copy characters/luna.yaml.example as a starting point:
name: Leo
trigger_word: LFPZ # short nonsense token — avoid real words
gender: boy
age_description: "toddler boy, about 3 years old"
ethnicity: "mixed Latino and East Asian descent, warm medium tan skin"
hair: "soft tight dark brown curls, kept short"
face: "very round chubby toddler face, big bright dark brown eyes, wide cheerful smile"
outfit: "sky blue zip-up romper with small patch pockets, white socks, white sneakers with Velcro straps"
# Style LoRA applied during test inference only (not during training image generation)
# style_lora_id: "civitai:512147@1271855"Key rules:
trigger_wordmust be a short nonsense string (e.g.ZNKL,BFPT). It's embedded in every training caption and used at inference time.outfitdescribes the canonical look. Keep it identical across all training images — outfit variation is handled at prompt time during inference, not in training.- Training images are generated against vanilla FLUX.1-dev with no style LoRA. Style is applied only at inference time, keeping the character LoRA style-agnostic and compatible with all your style LoRAs.
characters/
├── registry.json
└── <name>/
├── character.yaml canonical character config
├── reference.png reference image
├── training/ images + .txt captions submitted for training
├── lora/
│ └── <name>.safetensors downloaded weights
└── test/ test scene outputs
| RunPod | Replicate | Fal.ai | |
|---|---|---|---|
| Model | Flux.2 Klein 4B | Flux.1-dev | Flux.1-dev |
| Trainer | ostris/aitoolkit:latest (AI Toolkit) |
ostris/flux-dev-lora-trainer |
flux-lora-fast-training |
| Cost | ~$1.00–$1.50 / run | ~$1.85–$2.75 / run | ~$2.00 / run |
| Rank control | Yes | Yes (4–64) | No (fixed internally) |
| Turnaround | ~20–25 min | ~20–30 min | ~5–15 min |
| Recommended rank | 16 for characters | 16–32 for characters | — |
RunPod is the recommended provider — trains on Flux.2 Klein 4B which is 5x+ faster at inference. Replicate and Fal.ai produce good results on Flux.1-dev if RunPod is unavailable.
Runware downloads weights from a public URL — it does not accept direct file uploads. S3-compatible storage is used to stage the extracted .safetensors file and provide a presigned URL.
Cloudflare R2 setup:
- Endpoint format:
https://<account_id>.r2.cloudflarestorage.com - No egress fees, 10 GB free tier
- Set
S3_REGION=auto
Without S3, fal_client is used as a fallback staging layer (requires FAL_API_KEY).