Migration

Switch to Soup from other fine-tuning tools in one command. Soup automatically converts your existing config files.

Supported Tools

SourceConfig formatCommand
LLaMA-FactoryYAML configsoup migrate --from llamafactory config.yaml
AxolotlYAML configsoup migrate --from axolotl config.yml
UnslothJupyter notebooksoup migrate --from unsloth finetune.ipynb

Usage

bash
# Convert LLaMA-Factory config to soup.yaml
soup migrate --from llamafactory llama3_lora_sft.yaml

# Convert Axolotl config
soup migrate --from axolotl axolotl_config.yml

# Convert Unsloth notebook (AST-only, no code execution)
soup migrate --from unsloth finetune.ipynb

# Preview without writing (dry-run)
soup migrate --from llamafactory config.yaml --dry-run

# Custom output path
soup migrate --from axolotl config.yml --output my-soup.yaml

What Gets Mapped

Soup maps all major config fields automatically:

  • Model: model_name_or_path / base_model -> base
  • Task: stage / rl -> task (pt->pretrain, rm->reward_model, sft/dpo/kto/ppo/grpo), with the refinements below
  • LoRA: rank, alpha, dropout, target modules, DoRA
  • Training: epochs, lr, batch size, optimizer, scheduler, warmup, quantization
  • Quantization and full fine-tuning: since v0.76.0 an unquantized source writes quantization: none explicitly and a full fine-tune source writes lora.r: 0. HQQ and EETQ sources are supported
  • Data: dataset path, format, max length
  • Output: output directory

When the task is not a plain SFT or DPO (v0.76.0)

An Axolotl rl:, a LLaMA-Factory pref_loss: or an Unsloth CPOTrainer loss with no Soup task now stops the migration with exit 1, naming the value, instead of writing a task: sft config. No file is written. (An unrecognised LLaMA-Factory stage: still falls back to sft.)

  • Axolotl rl: maps dpo, ipo, kto, orpo and simpo to the task of the same name, and grpo and gdpo to task: grpo. dpo, ipo, orpo and simpo get the dpo data format, kto gets kto and grpo gets auto. Any other value is refused: Migration failed: No Soup task matches axolotl rl: ebft. Supported rl values are dpo, gdpo, grpo, ipo, kto, orpo, simpo.
  • LLaMA-Factory pref_loss: is read only under stage: dpo. sigmoid gives dpo, ipo gives ipo, orpo gives orpo and simpo gives simpo. hinge, kto_pair, any other value, or a value that is not a string is refused: No Soup task matches LLaMA-Factory pref_loss: hinge. Supported pref_loss values are ipo, orpo, sigmoid, simpo. Under a stage other than dpo the stage's own task is kept.
  • Unsloth notebooks take the task from the trainer class: SFTTrainer to sft, DPOTrainer to dpo, KTOTrainer to kto, ORPOTrainer to orpo, GRPOTrainer to grpo, PPOTrainer to ppo, RewardTrainer to reward_model, BCOTrainer to bco and OnlineDPOTrainer to online_dpo. An online DPO notebook migrates with a warning and a placeholder training.online_dpo_judge: ollama://REPLACE-ME, because TRL's judge is a Python object with no string form; replace it, or set training.reward_model. CPOTrainer migrates, to simpo, only when loss_type="simpo" is set on that trainer call or its args= config; any other CPO loss, or an args= that cannot be read statically, stops the migration.
  • The last trainer decides. When a notebook builds several trainers, the task, output directory and hyperparameters come from the config passed to the last one, inline, named or positional, so an SFT warm-up before a GRPO stage no longer leaks its num_train_epochs into the GRPO config. Upstream names four public Qwen3 4B GRPO notebooks that used to inherit training.epochs: 2.
  • IPython magics no longer break a notebook. Lines starting with ! or % (!pip install ...) are skipped; %%capture, %%time, %%timeit and %%prun lose only their header line and their body is still read; any other cell magic (%%bash, %%writefile) is skipped whole. A real syntax error is now reported with its code cell and line.
  • The output is validated with the config loader before it is written, and an Axolotl source with no adapter prints No adapter specified for task 'sft' - full fine-tuning will be used (lora.r: 0); a LLaMA-Factory source with finetuning_type: full and an Unsloth notebook with full_finetuning=True print their own full-fine-tuning warning. A full fine-tune of a task other than SFT or embedding (DPO, pretraining, reward model) is refused.

If you migrated before v0.76.0, check the generated soup.yaml. Upstream's changelog lists what changed in the result: an unquantized source had no quantization line, so Soup loaded it as 4-bit QLoRA, and a full fine-tune source got LoRA r=64; Axolotl rl: orpo, ipo and simpo became task: sft with no warning; LLaMA-Factory pref_loss: ipo became task: dpo; an Unsloth CPO, reward-model, BCO or online DPO notebook became task: sft; and a warm-up trainer's epochs could leak into the final stage. If your source was bf16 or a full fine-tune, or used any of those trainers or losses, the run you got may not have been the run you meant. Regenerate and diff.

Unsupported Features

Soup will warn about features that don't have a direct equivalent:

  • sample_packing (Axolotl) — not auto-mapped by soup migrate; set training.packing (or training.multipack) by hand after conversion
  • freeze training (LLaMA-Factory) — not auto-mapped; set training.freeze_layers or training.freeze_ratio by hand. Both keys are applied only by task: sft (and tts) on backend: transformers with modality: text and no layer streaming; any other combination is refused at config load (#1497, #1581)
  • use_rslora (Unsloth) — supported, but the key is nested: training.lora.use_rslora
  • max_steps — Soup uses epochs; max_steps emitted as comment
  • DeepSpeed / W&B — emitted as CLI flag comments

Security

  • Input files are validated to be within the current working directory
  • Unsloth notebooks are parsed via AST only — no code is executed
  • Output paths are confined to the current directory

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.