CLI tool for automated character LoRA training using cloud providers.
Currently supports generation of training images using Runware (FLUX.1-dev + Kontext) and FLUX.1-dev training on Replicate or Fal.ai. Resulting 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 (Replicate or Fal.ai), and S3-compatible storage (Replicate-trained models only)
# 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 | replicate (default) or fal |
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 FLUX.1-dev 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. |
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 |
Recommended | S3-compatible storage with publicly accessible URL for staging weights. |
S3_ACCESS_KEY_ID |
Recommended | S3 access key |
S3_SECRET_ACCESS_KEY |
Recommended | S3 secret key |
S3_BUCKET_NAME |
Recommended | 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 (Replicate ~20–30 min · Fal ~5–15 min · ~$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
| Replicate | Fal.ai | |
|---|---|---|
| Trainer | ostris/flux-dev-lora-trainer (ai-toolkit) |
flux-lora-fast-training |
| Cost | ~$1.85–$2.75 / run | ~$2.00 / run |
| Rank control | Yes (4–64) | No (fixed internally) |
| Turnaround | ~20–30 min | ~5–15 min |
| Recommended rank | 16–32 for characters | — |
Replicate is the default. Fal.ai is faster if you don't need rank control.
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).