LoRA Adapters
LoRA adapters let you specialize a base model at runtime without loading a separate copy. A single 1B–8B base can host many adapters and switch between them in milliseconds.
Two common patterns:
- Per-character adapters — one adapter per NPC, quest line, or language register. The base model handles language; the adapter shapes voice, style, and personality.
- Per-task adapters — one adapter per cognitive task: planning, decision-making, dialogue generation, summarization, intent classification. A single base model becomes a multi-skill backbone, with the active adapter selected per request based on what the game loop is currently asking for. Behaviour frameworks built on Atelico typically route a planner adapter, a decider adapter, and a dialogue adapter in turn during one NPC tick.
Atelico supports two on-disk adapter formats:
- Standard LoRA — the original Low-Rank Adaptation format. Each layer ships two factor
matrices
AandB(low-rank decomposition of the weight delta). - LoRA-XS (arXiv:2405.17604) — inserts a small
trainable
r×rmatrixRbetween two frozen SVD-derived factors of the base weight. OnlyRis trained, so the per-layer parameter count drops fromr·(d_in + d_out)to justr²(256 floats per layer at rank 16, regardless of model size). On-disk adapters can be ~100× smaller than standard LoRA at comparable quality.
Both formats use the same load/unload/scale API — the engine auto-detects which one you've handed it.
Loading an Adapter
LoRA management lives in the SDK layer — every binding (Godot, Unity, Unreal, Python,
C FFI, Rust SDK) exposes the same three operations: load, set scale, unload. The adapter
directory must contain an adapter_config.json (HuggingFace PEFT layout) and the
safetensors weight files.
Python example:
engine.lora_load(
"in-memory::meta-llama/Llama-3.2-1B-Instruct-Q4_K_M",
"/path/to/pirate-lora",
)
engine.lora_set_scale(
"in-memory::meta-llama/Llama-3.2-1B-Instruct-Q4_K_M",
0.7, # blend at 70% strength
)
engine.lora_unload("in-memory::meta-llama/Llama-3.2-1B-Instruct-Q4_K_M")
Once loaded, request the adapted variant by appending ::adapter-name to the model id in
your chat completion call (the adapter name is taken from the adapter_config.json
metadata):
response = engine.chat_completion(
model="in-memory::meta-llama/Llama-3.2-1B-Instruct-Q4_K_M::pirate",
messages=[{"role": "user", "content": "Greet the tavern."}],
)
See the per-SDK API references for the exact method signatures in each language.
Adapter Format Detection
The engine reads adapter_config.json and scans the safetensors keys to decide which
format you're loading:
- If
adapter_config.jsonsetspeft_typeto"LORA_XS"(case-insensitive), the adapter is treated as LoRA-XS. - Otherwise, the engine inspects the safetensors keys. Any tensor ending in
.lora_R.weight(or the reference-implementation alias.default_lora_latent_mapping.weight) marks the adapter as LoRA-XS. - If neither signal is present, the adapter is treated as standard LoRA.
No flag, environment variable, or API change is required to use LoRA-XS — drop the adapter into your asset store and load it like any other.
LoRA-XS On-Disk Variants
Two on-disk layouts are accepted for LoRA-XS adapters:
| Variant | Tensors per layer | Notes |
|---|---|---|
| A (full) | lora_A, lora_B, lora_R | Self-contained. Matches the reference implementation. |
| B (R-only) | lora_R only | Smallest on-disk size. A and B are reconstructed at load time from the base weight via a deterministic randomized truncated SVD. The reconstruction is cached per (model, rank), so subsequent adapter swaps of the same rank are instant. |
Variant B is the recommended layout for shipped game assets — a rank-16 R-only adapter on an 8B base model is well under 100 KB per layer.
Runtime Behavior
- One adapter is active per base model at a time. Loading a second adapter onto the same model replaces the first.
lora_set_scale(model, α)blends the adapter at strengthα(0.0= base model only,1.0= full adapter, intermediate values interpolate).- Switching adapters is in-memory; there is no GPU model reload.
- Inference throughput with an active adapter is within a few percent of the base model; the adapter forward path is a single fused matmul per attached projection.
See Also
- Models — list of supported base architectures
- NPC Dialogue — using per-character adapters to drive NPC personality