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

bash
soup migrate --from llamafactory path/to/your/llamafactory_config.yaml

This produces a soup.yaml file equivalent to your LLaMA-Factory config, mapping:

LLaMA-FactorySoup CLI
model_name_or_pathbase (a root-level string, not base.model)
stagetask: sft gives sft, pt gives pretrain, rm gives reward_model, and dpo, kto and ppo keep their names
finetuning_type: loratraining.lora.r (LoRA is on when r > 0)
finetuning_type: fulltraining.lora.r: 0 and training.quantization: none, a full fine-tune. Only stage: sft can do this: any other stage exits 1
lora_ranktraining.lora.r
lora_alphatraining.lora.alpha
quantization_bittraining.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_betatraining.dpo_beta for dpo, training.kto_beta for kto, training.orpo_beta for orpo
datasetdata.train: ./<dataset>.jsonl. The name is a LLaMA-Factory registry entry, so point it at the real file
per_device_train_batch_sizetraining.batch_size
cutoff_lendata.max_length (sequence length lives under data)
templatenothing: 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:

yaml
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: 3

Soup 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):

yaml
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: 32

Why migrate?

  • Faster training — backend: unsloth gives 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 export all in one tool
  • 170 ready-made recipes — soup recipes list
  • Built-in GGUF/Ollama export
  • Dry-run migration — preview the config with --dry-run before writing

Dry-run preview

bash
soup migrate --from llamafactory config.yaml --dry-run

Prints 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 quantization line, so Soup loaded it as 4-bit QLoRA, and a full fine-tune source got LoRA r=64 instead of a full fine-tune (#1215, #1302).
  • stage: dpo with pref_loss: ipo became task: dpo. A pref_loss of hinge, kto_pair or any other value became task: dpo with no warning; it now exits 1 and names the value (#1214, #1560).
  • pref_loss: orpo or simpo under a stage other than dpo became task: orpo or task: simpo; LLaMA-Factory reads pref_loss only under stage: 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.

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.