Best-of-N & Evol-Instruct (v0.71.31)
Two soup data subcommands that put an LLM judge in the loop to build training data, the data side of the v0.71.31 judge-in-the-loop suite.
We have not found this as an integrated CLI suite elsewhere.
soup data best-of-n — rejection sampling (BOND-lite)
Sample N completions from a base model, let a judge score each one, keep the winner as a clean SFT row. With --emit-pairs it also writes winner-versus-loser DPO pairs, so one pass bootstraps both an SFT set and a preference set.
soup data best-of-n --base HuggingFaceTB/SmolLM2-135M-Instruct \
--prompts prompts.jsonl --n 8 --judge ollama://llama3.1 \
-o best_of_n.jsonl --emit-pairs pairs.jsonl| Flag | Default | Meaning |
|---|---|---|
--base | the default path | Local model to sample from (loads locally; bounded by your GPU). Give this or --provider. |
--provider | off | v0.74.0. ollama or vllm: draw the candidates from a running raw-completion endpoint instead of loading a model. anthropic is refused by name, because its Messages API has no raw-completion endpoint. |
--model | required with --provider | Provider-side model id, recorded in _best_of_n provenance next to the provider. |
--base-url | loopback | Provider endpoint, SSRF-validated. |
--prompts | required | Prompt JSONL (cwd-contained, symlink-rejected). |
--judge | required for the online workflow | Judge URL, SSRF-validated. Not accepted with --export-candidates or in the offline phase, which construct no judge. |
--n | 8 | Candidates per prompt (2..64). |
-o, --output | required | SFT output JSONL; each row carries a _best_of_n provenance object (n, winner_idx, judge_model, scores). |
--emit-pairs | off | Also write winner-vs-loser DPO pairs to this path. |
--temperature | 1.0 | Sampling temperature (0..2). |
--max-new-tokens | 256 | Tokens per candidate (1..4096). |
--seed | 0 | Sampling seed. |
--plan-only | off | Print the plan and exit without sampling. |
--revision | off | Pin the revision of the local --base model, as the two-phase example below does. |
--manifest | <output>.manifest.json | Where the commit manifest is written (last, after the outputs). |
Judging is pointwise (one judge call per candidate, argmax), so cost scales with --n. Prompt rows are validated with line-numbered errors instead of being silently dropped, each accepted row records its source_line, and a non-finite or boolean judge score is rejected before a winner is picked (v0.74.0).
Sampling from a provider, and finishing what you started (v0.74.0)
The candidates no longer have to come from a model you load. --provider ollama|vllm --model <id> [--base-url <url>] draws them from a running raw-completion endpoint through the same SSRF-validated seam soup data evolve uses. The local-only flags are refused in provider mode rather than silently ignored.
A run that dies partway no longer starts over. Sampling is journalled per prompt (--checkpoint, defaulting beside the output), --resume continues from the last committed group, and the outputs are published manifest-last, so an interruption never leaves a partial SFT or DPO file behind. A resume binds the prompt source lines and the exact local model content, so it refuses to continue against changed inputs instead of quietly mixing two runs.
For an air-gapped or two-phase workflow, split sampling from judging:
# Phase 1, where the GPU is: sample only, construct no judge.
soup data best-of-n --base Qwen/Qwen2.5-7B-Instruct --revision <commit> \
--prompts prompts.jsonl --n 8 --export-candidates candidates.jsonl
# Phase 2, anywhere: materialize the outputs from verified judgments,
# with no model load and no network.
soup data best-of-n --candidate-artifact candidates.jsonl \
--judgments verified.jsonl -o best_of_n.jsonl --emit-pairs pairs.jsonlverified.jsonl holds one JSON object per candidate group, copying prompt_id and group_digest from candidates.jsonl, with one score per candidate (so the --n 8 above needs eight):
{"prompt_id": "...", "group_digest": "...", "winner_idx": 2, "scores": [0.1, 0.4, 0.9, 0.3, 0.2, 0.5, 0.6, 0.1], "verifier": {"name": "my-checker", "version": "offline-v1"}}Anything that can write that line can be the judge: a person, a deterministic program or a CI job. The offline phase fails closed before writing anything on a missing or duplicate prompt id, a changed group digest, an invalid winner index, a score-count mismatch, a non-finite score, or a winner that disagrees with the scores. It writes <output>.manifest.json (or the --manifest path) last, binding the SHA-256 and row count of each output; that manifest is the commit marker, so a consumer should open exactly the files it lists and never glob neighbouring JSONL. --resume and --checkpoint are rejected here because nothing is sampled: after a late write failure, keep the two inputs and rerun the same command. The online recovery journal (<output>.checkpoint.jsonl) contains dataset content, so protect it like the dataset.
Breaking, if you script it:
--export-candidates --seed Nseeds each prompt independently as of v0.74.0, so a resumed run reproduces the same candidates. That changes the output of an existing--export-candidates --seed Ncommand.
soup data evolve — Evol-Instruct (WizardLM)
Grow instruction diversity by mutating seed prompts, completing the synthetic-data suite (Magpie, Forge, Persona, evolve). depth deepens an instruction (adds constraints, concretizes, adds reasoning steps); breadth creates a new sibling instruction in the same domain.
soup data evolve --input seeds.jsonl --provider ollama --model llama3.1 \
--strategy depth --rounds 2 -o evolved.jsonl| Flag | Default | Meaning |
|---|---|---|
--input | required | Seed instructions JSONL. |
--provider | required | ollama or vllm (the raw-completion providers; anthropic is rejected). |
--model | required | Generator model id. |
--strategy | depth | depth (deepen) or breadth (diversify). |
--rounds | 1 | Evolution rounds (1..5). |
-o, --output | required | Evolved output JSONL. |
--max-tokens | 512 | Generation budget (1..16384). |
Each round evolves every live instruction and drops empty, unchanged, or meta-prompt-echo outputs; if a round eliminates everything, the previous generation carries forward.
Security
Both write atomically (mkstemp + os.replace) with cwd containment re-validated to close a symlink-swap window; every judge / provider URL is SSRF-validated (since v0.75.2 an IP address in a private, link-local or reserved range is refused on https as well as http, so name a LAN server by hostname; what v0.75.2 refuses); --base loads with trust_remote_code=False (probe + warn); dataset-derived text is escaped before it is echoed. Prompts / seeds are capped at 100,000 rows.
See also
- Online DPO — train on-policy against the same judge.
- soup ship — decide SHIP with a pairwise judge win-rate.
- Data Forge & quality moat and Data engineering pro — the rest of the synthetic-data and quality surface.
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.