Migrate from LLaMA-Factory to Soup CLI
Switching from LLaMA-Factory to Soup CLI takes one command. Soup CLI offers a simpler YAML schema, 2–5× faster training via Unsloth, and a unified CLI for training, serving, and export.
One-line migration
soup migrate --from llamafactory path/to/your/llamafactory_config.yamlThis produces a soup.yaml file equivalent to your LLaMA-Factory config, mapping:
| LLaMA-Factory | Soup CLI |
|---|---|
model_name_or_path | base (a root-level string, not base.model) |
stage | task: sft gives sft, pt gives pretrain, rm gives reward_model, and dpo, kto and ppo keep their names |
finetuning_type: lora | training.lora.r (LoRA is on when r > 0) |
finetuning_type: full | training.lora.r: 0 and training.quantization: none, a full fine-tune. Only stage: sft can do this: any other stage exits 1 |
lora_rank | training.lora.r |
lora_alpha | training.lora.alpha |
quantization_bit | training.quantization: 4 gives 4bit, 8 gives 8bit; with quantization_method: hqq it gives hqq:Nbit (N of 1, 2, 3, 4, 5, 6 or 8), with eetq it gives eetq (8 only). No quantization_bit gives none |
pref_loss (read only under stage: dpo) | task: sigmoid gives dpo, and ipo, orpo and simpo give the task of the same name. Any other value exits 1 |
pref_beta | training.dpo_beta for dpo, training.kto_beta for kto, training.orpo_beta for orpo |
dataset | data.train: ./<dataset>.jsonl. The name is a LLaMA-Factory registry entry, so point it at the real file |
per_device_train_batch_size | training.batch_size |
cutoff_len | data.max_length (sequence length lives under data) |
template | nothing: Soup auto-detects the chat template from the tokenizer. Override with data.chat_template if you need to pin one. |
If lora_rank or lora_alpha is missing from the source, the importer writes r: 64 and alpha: 16; set both in the source if you want other values. A finetuning_type of freeze or oft has no Soup equivalent: the importer warns and writes LoRA.
Side-by-side example
LLaMA-Factory config:
model_name_or_path: meta-llama/Llama-3.1-8B-Instruct
stage: sft
do_train: true
finetuning_type: lora
lora_rank: 16
lora_alpha: 32
dataset: alpaca_en
template: llama3
cutoff_len: 2048
per_device_train_batch_size: 2
gradient_accumulation_steps: 8
learning_rate: 2.0e-4
num_train_epochs: 3Soup CLI equivalent (soup migrate itself writes format: auto, target_modules: auto, quantization: none and no backend line, and warns that alpaca_en is a LLaMA-Factory registry name; backend: unsloth and format: alpaca below are hand-written):
base: meta-llama/Llama-3.1-8B-Instruct
task: sft
backend: unsloth # root-level, NOT under training
data:
train: ./alpaca_en.jsonl # LLaMA-Factory dataset names are registry entries; point at the real file
format: alpaca
max_length: 2048 # sequence length lives under data
training:
quantization: none # the source has no quantization_bit; Soup's default would be 4-bit QLoRA
epochs: 3
lr: 2.0e-4 # the key is lr, not learning_rate
batch_size: 2
gradient_accumulation_steps: 8
lora:
r: 16 # LoRA turns on when r > 0; there is no enabled flag
alpha: 32Why migrate?
- Faster training —
backend: unslothgives 2–5× speedup on Llama/Qwen/Gemma/Mistral - Single YAML schema validated by Pydantic v2 (no silent typos)
- Unified CLI —
soup train,soup chat,soup eval,soup serve,soup exportall in one tool - 170 ready-made recipes —
soup recipes list - Built-in GGUF/Ollama export
- Dry-run migration — preview the config with
--dry-runbefore writing
Dry-run preview
soup migrate --from llamafactory config.yaml --dry-runPrints the converted config to stdout without writing. Without --dry-run, soup migrate writes soup.yaml in the current directory (--output picks another path) and asks before it overwrites a file that exists (--yes skips the question). It checks the result with Soup's own config loader before writing, so the file it hands back loads.
Then run soup doctor --config soup.yaml to see the loader's errors and warnings for the result. Its list of settings it knows to be unread is short, so a clean report is not a guarantee that every key takes effect.
If you migrated before v0.76.0
Check the soup.yaml you generated, because the importer changed what it writes. Upstream's changelog lists three results that were wrong:
- An unquantized source wrote no
quantizationline, so Soup loaded it as 4-bit QLoRA, and a full fine-tune source got LoRAr=64instead of a full fine-tune (#1215, #1302). stage: dpowithpref_loss: ipobecametask: dpo. Apref_lossofhinge,kto_pairor any other value becametask: dpowith no warning; it now exits 1 and names the value (#1214, #1560).pref_loss: orpoorsimpounder a stage other thandpobecametask: orpoortask: simpo; LLaMA-Factory readspref_lossonly understage: dpo, so the stage's own task is kept now.
If your source was bf16 or a full fine-tune, or used one of those losses, the run you got may not have been the run you meant. Regenerate and diff. The same applies to Axolotl and Unsloth sources, see Migration.
Related
Soup is free and Apache-2.0. If it saved you a training run, starring the repo costs nothing and helps most. You can also fund the GPU time behind the work a 4 GB laptop cannot reach.