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.

bash
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
FlagDefaultMeaning
--basethe default pathLocal model to sample from (loads locally; bounded by your GPU). Give this or --provider.
--provideroffv0.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.
--modelrequired with --providerProvider-side model id, recorded in _best_of_n provenance next to the provider.
--base-urlloopbackProvider endpoint, SSRF-validated.
--promptsrequiredPrompt JSONL (cwd-contained, symlink-rejected).
--judgerequired for the online workflowJudge URL, SSRF-validated. Not accepted with --export-candidates or in the offline phase, which construct no judge.
--n8Candidates per prompt (2..64).
-o, --outputrequiredSFT output JSONL; each row carries a _best_of_n provenance object (n, winner_idx, judge_model, scores).
--emit-pairsoffAlso write winner-vs-loser DPO pairs to this path.
--temperature1.0Sampling temperature (0..2).
--max-new-tokens256Tokens per candidate (1..4096).
--seed0Sampling seed.
--plan-onlyoffPrint the plan and exit without sampling.
--revisionoffPin the revision of the local --base model, as the two-phase example below does.
--manifest<output>.manifest.jsonWhere 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:

bash
# 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.jsonl

verified.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):

json
{"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 N seeds 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 N command.

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.

bash
soup data evolve --input seeds.jsonl --provider ollama --model llama3.1 \
    --strategy depth --rounds 2 -o evolved.jsonl
FlagDefaultMeaning
--inputrequiredSeed instructions JSONL.
--providerrequiredollama or vllm (the raw-completion providers; anthropic is rejected).
--modelrequiredGenerator model id.
--strategydepthdepth (deepen) or breadth (diversify).
--rounds1Evolution rounds (1..5).
-o, --outputrequiredEvolved output JSONL.
--max-tokens512Generation 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

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.