v0.77.2: the token rule and explicit refusals
v0.77.2 was tagged on 5 November 2026. Upstream gave it no codename and titled it with a sentence again: a cut of main: explicit refusals, MLX preference and GRPO, corrected VRAM estimates and server replies. It is a cut of the development branch after v0.77.1, not a backport: it carries every pull request merged since that tag. By the CHANGELOG's count that is 6 additions, 5 changes, 2 removals and 117 fixes. The release body adds: "No gate ran for this release and the preprint is unchanged."
153 of the 159 pull requests in this release came from 30 people outside the maintainer. That is the release body's own count; the names are under Credits.
This is not a security release. No security advisory is published with it. Its Security section says only that the release "includes security hardening fixes; details follow through the project's advisory process". The supported versions are unchanged: 0.77.x is supported, 0.76.x receives security fixes only until 0.78.0 is released, and anything below 0.76 is not supported.
Coming from v0.76.x or older? Read v0.77.0 first. A config that loaded on v0.76 can stop loading on v0.77, and v0.77.2 changes none of that. Run
soup doctor --config soup.yamlbefore the first run of an existing config.
What to do
pip install -U soup-cli
soup --version
soup doctor --config soup.yamlWith pipx, pipx upgrade soup-cli replaces the first line. soup --version prints soup v0.77.2. A soup.yaml that loads on v0.77.1 loads on v0.77.2, with one exception: a task: sft config that sets one of four settings on a path that never applied it. Five kinds of setup need a look:
- A
soup servetoken that does not follow the token rule now stops the server.--tool-auth-token(orSOUP_TOOL_AUTH_TOKEN) must be 16 to 128 characters fromA-Z,a-z,0-9,_and-; anything else exits 2 before the model loads, on every bind. See The token rule for soup serve. - A
task: sftconfig withreasoning_effort,train_on_eot,use_flash_attnoruse_ligeron a path that never read it now stops at load. Delete the line the message names. See Four settings that task sft refuses. - A CI step that tests an exit code may now see a different number.
soup planandsoup applyon a YAML syntax error exit 2 instead of 1, andsoup eval quant-checkon an unparseable--tasksfile exits 3 instead of 1.soup adapters bisectwith a program that cannot be started exits 2 with no verdict, where one that{ckpt}completed to a missing file used to end in aBROKEN_ATverdict, which also exits 2: the code is the same, so read the first line of its output. See Exit codes that moved. - A script that wrote a result over its own input now fails for more
soup datacommands,soup data ingestamong them, and forsoup adapters arithmeticandmerge. Write to a new path. See Outputs that cannot overwrite an input. - A shell completion script written before v0.77.2 does not complete. Run
soup completions bash,zshorfishagain and replace the installed script (#2283).
Three more things are worth a look, none of them forced:
- Two finished-run items. An MLX run that used
training.optimizer: adafactor, and a pairs file built bysoup data from-traces --signal thumbs_upor by the stage ofsoup loopthat harvests serve trace logs. See Re-run this. - A
soup mergewithHF_HUB_OFFLINEset now needs the recorded base commit in the cache. See soup merge pins the base commit. - The VRAM pre-flight prices some runs differently, and can now refuse a short full fine-tune or a short NF4 layer-streamed run it used to let through.
--allow-oom-attemptstill launches it. See The VRAM estimate prices some runs differently.
Coming from v0.76.x or older, Python 3.10 stays on v0.76.x (pip install "soup-cli[train]<0.77"), and AWQ and GPTQ export were removed in v0.77.0. See Docker and install for the image.
Re-run this
| What was wrong | Who is affected | PR |
|---|---|---|
Upstream: "MLX Adafactor (backend: mlx, training.optimizer: adafactor) applies the configured learning rate, warmup and schedule, so an existing run trains differently." Through v0.77.1 those three settings were not applied to it. | A run with backend: mlx and training.optimizer: adafactor, trained on v0.77.1 or earlier. Re-run it if you want the run the config describes. Only this slice of the settings a backend never applies is fixed; the rest of that issue stays open. | #2484 by @vaputa; the rest is tracked as #1811 |
Upstream: soup data from-traces --signal thumbs_up, "and the harvest stage of soup loop, pair a thumbs-up and a thumbs-down answer that differ, and skip a prompt whose answers are all the same text; 0.77.1 could write a pair whose chosen and rejected were identical, so the number of pairs from the same logs can change." | A pairs file written by soup data from-traces --signal thumbs_up, or by the stage of soup loop that harvests serve trace logs, on v0.77.1 (the release notes name no earlier version). To see whether a file you hold is affected, run soup data lint on it: its identical_pairs check counts, among the rows it samples (--sample, default 2000), those where chosen equals rejected; pass a larger --sample for a bigger file. Re-run the harvest if it is. | #2495 by @kok-o |
These are the two items in the release notes that change what an already finished run or file holds; upstream lists them under its changes that alter a result, and frames nothing else in this release as a wrong finished artifact. The refusals below apply from v0.77.2 on.
Check your output
Several commands used to exit 0 with a result that was empty, unfiltered or wrong, and now stop. There is no re-run row for them, because what to check is a file you already hold, not a trained model. In the release notes' own words for the old result:
soup distill-promptwith no usable prompt in any trace left an "empty output written". It now exits 3 and leaves the output untouched (#2209).soup data evolveandsoup data gen-magpiewhen no row was produced and at least one provider call failed left the "output emptied" (the release table words the case as "every provider call failed"; the code at the tag tests for no row and at least one failed call). They now exit 1 and keep the output (#2348).soup data filter --perplexityand--score-onlywithout torch or transformers exited 0 with "unfiltered or partial" output. They now exit 1 (#2377).soup data dedup --field <key>with the key absent from every row exited 0 with "distinct rows dropped". It now exits 1 (#2336).
If one of these ran in a script against v0.77.1, open what it wrote. A command that overwrote its own input is a different case and has no row either: the input is gone, so the advice is to write to a new path (see Outputs that cannot overwrite an input).
The token rule for soup serve
Since v0.77.2 (#2329) soup serve checks --tool-auth-token (or SOUP_TOOL_AUTH_TOKEN) with the rule soup ui and soup mcp serve already use: 16 to 128 characters from A-Z, a-z, 0-9, _ and -. v0.77.1 accepted any non-empty value. Any other value now exits 2 before the model loads, on every bind, loopback included, whether it comes from the option or from the environment variable. The message names the rule and never prints the value. All three commands now match the whole value, so a valid token followed by a newline is refused by all three.
soup serve --model ./output --tool-auth-token shortError: --tool-auth-token (or SOUP_TOOL_AUTH_TOKEN): token must be 16-128 urlsafe-base64 chars (A-Z, a-z, 0-9, '_', '-'). Generate one with: python -c "import secrets; print(secrets.token_urlsafe(32))"That run exits 2 whatever ./output holds, because the check comes first. To get a token that passes, generate one and send the same value as Authorization: Bearer <token>:
python -c "import secrets; print(secrets.token_urlsafe(32))"It prints 43 characters. The rule changes what the token may look like, not which routes carry it; that is described on Inference server.
Other changes to soup serve
- The vLLM, SGLang and DeepSpeed-MII servers answer a generation request whose
Originnames another site with 403, as the transformers server did (#2329). On a LAN or wildcard bind, a browser page from another origin now gets 403 from these apps too; a request with noOriginheader is served as before. Upstream lists this check among the things not exercised by hand before the release; it is covered by tests (see Known limitations). Upstream's notes do not describe it as a vulnerability fix. - The notice printed for a non-loopback bind now also says that requests travel as plain HTTP and that TLS belongs in a proxy in front (#2329).
- A reply cut off at
max_tokenswithout an end token now ends withfinish_reason: "length"on/v1/chat/completionsandstop_reason: "max_tokens"on/v1/messages, on the transformers backend and the MoLE runtime (#2380 by @abdulwaarith0). This page says nothing about what the other backends report. /v1/messagesapplies the request'stemperatureandtop_p, and a value out of range ornullis a 400 instead of the default (#2388 by @MbappeWU). Through v0.77.1 the converter dropped both fields on the transformers and vLLM servers, so every/v1/messagesrequest was sampled at the server defaults (0.7 and 0.9) whatever it sent,temperature: 0included: outputs collected through this route on v0.77.1 or earlier were not produced at the settings the request named./v1/chat/completionsalways read them./v1/tools/pythoncompiles your code under the file name<code>, so a traceback gives your own line numbers (File "<code>", line 2for an error on the second line); through v0.77.1 they counted the sandbox's network guard (five lines and a blank one) and the first line you sent was line 7. When the sandbox cannot start,/v1/tools/bashanswers 501 withbash sandbox requires OS-level isolation, as/v1/tools/pythonanswers with its own fixed sentence (#2329). Both were read in the code, not run over HTTP: the route answers 501 on Windows.
Four settings that task sft refuses
task: sft now refuses four training. settings on the paths that never applied them (#2278 by @abdulwaarith0). v0.77.1 loaded the config and trained without the setting; v0.77.2 stops it at load and names the field, the setting that selects the path and what to change. Leave the key out. The three booleans (use_flash_attn, use_liger, train_on_eot) also load when set to false, and reasoning_effort loads when it is null; the other values fail before the path is checked (null for a boolean: Input should be a valid boolean; reasoning_effort: false: reasoning_effort must not be bool, got False). The release notes say "a value of false or null loads", which holds for neither kind of setting. The five-path matrix is also on Settings Soup refuses at load.
| Path | Refused when set |
|---|---|
modality: vision or modality: audio | reasoning_effort, train_on_eot, use_flash_attn, use_liger |
backend: mlx | reasoning_effort, train_on_eot (use_flash_attn and use_liger still load, but soup train stops on them before the MLX trainer starts: CUDA-only checks, exit 1) |
backend: unsloth | use_flash_attn, use_liger |
data.format: raft | reasoning_effort, train_on_eot |
data.format: pre_tokenized | reasoning_effort (train_on_eot loads: the cache is keyed on it) |
stream_layers: true and the other tasks are unchanged. A config like this stops:
base: org/model
task: sft
backend: unsloth
data:
train: ./d.jsonl
training:
use_flash_attn: trueConfig validation error:
training.use_flash_attn is not applied by task='sft' with backend='unsloth': the unsloth setup loads its own model, with no Liger patch and no attn_implementation, so this run would ignore it. Use backend: transformers, or remove the key.soup train exits 1, soup doctor --config exits 2 with Config could not be loaded (see above)., and soup plan and soup apply exit 1 with the same message. The same config with use_flash_attn: false loads. Remove the key, or move the run to the path that reads it (here backend: transformers). Removing the line changes nothing about a run that already finished, so there is no re-run row for this: v0.77.1 trained without the setting. soup migrate writes no backend: key, so its output does not meet the backend: unsloth row; a config you edited by hand to add that backend alongside use_flash_attn: true does.
The load-time refusals as a whole, with the start of each message, are on Settings Soup refuses at load.
Exit codes that moved
The release notes list ten changes, reproduced here in their own words for the old and the new result, except that the evolve / gen-magpie case is worded as the code at the tag tests it (the release table says "every provider call failed"). The same rows are on Exit codes:
| Command and case | 0.77.1 | 0.77.2 |
|---|---|---|
soup eval quant-check, unparseable --tasks file | 1, after loading both models | 3, before (#2185) |
soup distill-prompt, no trace row has a prompt | 0, empty output written | 3, output untouched (#2209) |
soup data evolve / gen-magpie, no row produced and at least one provider call failed | 0, output emptied | 1, output kept (#2348) |
soup data filter --perplexity / --score-only without torch or transformers | 0, unfiltered or partial | 1 (#2377) |
soup data dedup --field <key> absent from every row | 0, distinct rows dropped | 1 (#2336) |
soup registry push --run-id on a run that is not completed | 0, entry registered | 1 (#2248) |
soup plan / soup apply, malformed YAML or a config that cannot be read | 1, with the raw parser or OS error | 2, naming the config and the YAML location (#2323) |
soup quantize --to "[/]" (a value Rich read as markup) | 1 | 2 (#2217) |
soup ship --progress jsonl, Ctrl+C | 1 | 130 (#2417) |
soup loop status, stored spend NaN or Infinity | 0, printed $nan | 2 (#2424) |
Four of these were run on the v0.77.2 binary for this page: soup plan and soup apply on a malformed soup.yaml print Config 'bad.yaml' is not valid YAML: expected ',' or ']', but got ':' (line 2, column 5) and exit 2, while a config file that does not exist still exits 1; soup eval quant-check on a --tasks file that is not a .jsonl or holds invalid JSON prints Cannot load --tasks: ... and exits 3 before either model is loaded; soup distill-prompt on traces with no prompt exits 3 and leaves an existing output file as it was; and soup quantize --to "[/]" exits 2 with a message that shows the value as typed. The other rows are the release notes' own and were not run here. The soup ship row is read from the code (upstream did not press Ctrl+C by hand before the release); see soup ship.
Also changed, outside that table:
soup ci init --pythonoutside 3.11 to 3.13 exits 1 withpython_version 3.10 is not supported by soup-cli 0.77.2 (needs Python 3.11 to 3.13)(#2249 by @Guts1005).03.11and3.011are still accepted and written as typed (tracked as #2534).soup eval compareandsoup eval leaderboardrefuse a stored score that is not finite, with exit 1 (#2477 by @taljeon).soup adapters bisect --eval-commandexits 2 with no verdict when its program cannot be started (#2314 by @Nari-Ab). A fixed program name is resolved before the first probe. A program name that contains{ckpt}is resolved for each probe, so that refusal can come after earlier probes ran. Through v0.77.1 a program that{ckpt}completed to a missing file counted as a failed checkpoint and ended in aBROKEN_ATverdict;BROKEN_ATalso exits 2, so a CI wrapper has to read the first line of the output and not only the code. On Windows the template is split without POSIX quoting: a word in single quotes stays one argument with the quote characters kept in it, a quote inside a word does not group the words after it, and only a word wrapped whole in double quotes is unwrapped. Write"{ckpt}"in double quotes (tracked as #2488). The splitting function was run directly on these inputs on the v0.77.2 binary. On every platform the template is split once, before{ckpt}is filled in, and{ckpt}is replaced inside each word, so a checkpoint id with spaces or quotes stays one argument.
The silent-pass risk is the one the v0.76.0 move had: a CI step that tests for exactly the old number stops matching. A step that tested for exit 1 on soup eval quant-check with a bad tasks file now sees 3, one that tested for exit 1 on a YAML typo in soup plan or soup apply now sees 2, and one that expected 0 from soup data evolve when no row was produced and a provider call failed now sees 1. The eleven gate and verdict commands keep the scheme on Exit codes: 0 pass, 2 gate failure, 3 usage or input error. soup eval quant-check is one of them, and an unparseable tasks file now joins its other usage errors at 3. soup distill-prompt exiting 3 does not make it a twelfth.
Outputs that cannot overwrite an input
v0.77.1 made eighteen soup data commands refuse an --output that is an input, and left a short list unchecked (#2226). v0.77.2 closes that list and adds the adapter commands. In the release notes' words, soup data ingest <document> --output <same document> replaced the document with JSONL; soup data persona-mix, generate, canary check, mix --optimize, forge and recipe --execute, and soup adapters arithmetic and merge with an --output that is an input adapter, overwrote their input. They now refuse before reading, with one shared terminal-safe message that covers links and alternate spellings of the path (#2274, #2349, #2379):
soup data persona-mix --prompts prompts.jsonl --n 2 --output prompts.jsonl--output 'prompts.jsonl' is the same file as --prompts 'prompts.jsonl'; choose a different --outputThat run exits 1 and prompts.jsonl is untouched. soup data ingest also refuses an output that names its source document before it reads it, and soup adapters arithmetic and soup adapters merge print Output path refused: --output 'c1' is the same file as adapter ...; choose a different --output and exit 1. The release notes give exit 1 for the new refusals (run here for persona-mix, ingest, arithmetic and merge), and the commands v0.77.1 already covered keep their codes for this refusal: soup data lint 3, soup data augment and soup data evolve 2 (evolve exits 1, not 2, when no row is produced and a provider call fails). The message no longer starts with Error: in soup data doctor and soup data lint, or with Plan failed: in soup data forge.
Write the result to another path, and rename it afterwards if you wanted an in-place edit. The refusals do not restore an input that an earlier version already replaced, which upstream said could not be recovered. This page makes no count of the commands that refuse: v0.77.1's "eighteen" was the release body's count for that release, and v0.77.2 prints no new total.
MLX preference and GRPO
Two refusals are lifted on Apple Silicon, and one is reworded. Both new paths are covered only by the hosted macOS MLX test job; upstream did not exercise MLX by hand before the release, and this page was written on a machine with no Apple Silicon: the config examples below were loaded and dry-run, nothing was trained. This page quotes no MLX speed, memory, time or accuracy figure, because no record holds one.
task: preferencewithtraining.preference_loss: dpo,orpoorsimporuns the native trainer (#2333 by @vaputa). The config keepstask: preference, andadapter_config.jsonrecords the native task, so a resume matches whichever of the two spellings the config uses. The tasksdpo,orpo,simpoandktoalready trained natively on MLX since v0.77.0; what is new is this spelling of the first three.ipo,bcoandpreference_loss_weightsblends stay refused:preference_loss: ipoonbackend: mlxstill stops at load withMLX backend only ships SFT, DPO, ORPO, SimPO, KTO and GRPO; task='preference' has no trainer on backend='mlx'.- MLX DPO
--resumerefuses an adapter of another task or base, an MLX SFT adapter included (#2321 by @mayuriphad). task: grpotrains on a base pre-quantized by mlx-lm at 4 bits inaffinemode (mlx-community/...-4bit, made withmlx_lm.convert -q) (#2386 by @ybai08). The quantized layers stay frozen and only the LoRA adapter trains. Any other quantized base is refused right after the load, naming the first layer outside the rule, andadapter_config.jsonrecordsbase_quantization. In v0.77.1 MLX GRPO needed an unquantized base.quantization: 8bitstays refused at load, with a reworded message:MLX GRPO trains a dense base or one pre-quantized at 4 bits (affine), not an 8-bit one.
base: mlx-community/Qwen2.5-0.5B-Instruct-4bit
task: grpo
backend: mlx
data:
train: ./d.jsonl
training:
quantization: 4bitThis config passes soup doctor --config (exit 0) and soup train --dry-run --yes on the light install used for this page; the check that the base really is a 4-bit affine one happens after the model loads, which was not run. MLX GRPO still refuses --resume, 8-bit and mixed-bit bases, mxfp4, mxfp8 and nvfp4, quantized mixture-of-experts and BitNet layers, lora.r: 0, custom or ensemble reward_fn settings and non-text modalities. The mlx extra's declared floor is tracked in #2471. MLX now trains six tasks (sft, dpo, orpo, simpo, kto, grpo) plus task: preference for dpo, orpo and simpo; the backend's page is Apple Silicon MLX backend.
soup plan and soup apply take --minillm-on-policy
soup plan and soup apply now take --minillm-on-policy (#2315 by @Nari-Ab), so an on-policy MiniLLM distill config is validated and hashed like any other, without rewriting the YAML. soup apply keeps the flag in the train command it suggests and still only records the plan; it does not start training. In v0.77.1 a distill config with minillm_enabled: true and minillm_teacher_mix_ratio: 0 needed training.minillm_on_policy: true in the YAML for these two commands.
soup plan --config distill.yaml --minillm-on-policy
soup apply --config distill.yaml --minillm-on-policyWith a task: distill config that names a training.teacher_model and sets training.minillm_enabled: true and training.minillm_teacher_mix_ratio: 0, the first line writes the plan and exits 0, and the same command without the flag exits 1 with the validation error that soup train gives. The second line ends with Now run: soup train --config distill.yaml --minillm-on-policy and This records the plan; it does not start training. A YAML syntax error or an unreadable config in either command is exit 2 now, as in Exit codes that moved.
soup profile without a soup.yaml
soup profile --model answers "will it fit?" without a soup.yaml (#2285 by @abdulwaarith0). Give it a Hugging Face id or a local model directory. It reads the model's own config.json from that directory, the Hugging Face cache or the Hub, and prices the run you set with --seq, --batch, --method lora|qlora|full, --lora-r, --optimizer and --gradient-checkpointing: the same estimate as a soup.yaml describing that run. --model and --config cannot be used together.
soup profile --model ./my-model --seq 2048 --batch 4 --method qlora --lora-r 16On a directory that holds a config.json this exits 0 and prints Parameter count from config.json (no safetensors headers were read). under the table. When no config.json can be read it prints no estimate and exits 1 with the reason:
Cannot read ./m: there is no readable config.json in that directory. Nothing was estimated: --model needs the model's own config.json, not a size guessed from its name.Also changed for soup profile: on Apple Silicon it and the MCP profile tool compare the estimate with Metal's recommended working-set size and not the whole unified memory, and soup profile --json returns lora_r and optimizer_step_memory_gb fields (the MCP profile tool returns lora_r too) and keeps its diagnostics on stderr so stdout stays strict JSON (#2398); soup cost --json does the same (#2345); both by @Nari-Ab. This was run here on a local directory with a toy config.json, not on a Hub model and not on Apple Silicon, and this page prints no estimate from it.
The VRAM estimate prices some runs differently
The one estimate behind soup train's pre-flight, soup profile, the MCP profile tool and soup plan (v0.77.1) changed in six places. A number it printed before can be a different number now:
- The optimizer step is priced as its own phase (#2338 by @abdulwaarith0). A short full fine-tune with
adamw_torchis priced higher, so inside the fitted range the pre-flight can now refuse a run it let through.--allow-oom-attemptstill launches it. - A sharded run is priced per card (#2313 by @ybai08): a run sharded across more than one process (
--gpus Nwith--fsdp full_shard,full_offloadorshard_grad, or DeepSpeed ZeRO-2 or ZeRO-3). The estimate was fitted on single-card runs, so for such a run it warns instead of refusing, unless the per-card floor alone (weights, context and the state of the trained parameters) is larger than the card, which still refuses;--allow-oom-attemptlaunches it anyway. The release body words this as "only warned about", and the code at the tag decides: it refuses when the floor exceeds the card. - A
task: prmstep is charged the logits of one position, so it is priced lower (#2337, #2372 by @ybai08). - A
stream_loss_chunkstep is charged the larger of the loss phase and the last layer's checkpoint recompute (#2290 by @ybai08), so it is priced higher. The larger-of-two charge applies to every layer-streamed run, not only tostream_loss_chunk: upstream says a short NF4 run without the option can be predicted higher, and a streamed run is refused when its prediction exceeds the free VRAM, so a run that started on v0.77.1 on a tight card can be refused now. The streaming-side settings for that decision arestream_vram_overrideandstream_vram_probe(see Pre-flight and tooling). Upstream measured the change once, on one model and one card (Llama-3.1-8B NF4, batch 1, one RTX 3060 Laptop 6 GB under WSL2), for separate q/k/v/o and gate/up/down projections only. It does not cover fusedgate_upprojections, mixture-of-experts layers or LoRA on MLP modules, and nobenchmarks/record holds the table, so this site prints no figure from it. backend: unslothis priced with the seven automatic LoRA targets and 16-bit embeddings and output head its loader uses (#2376 by @ryznren). That rests on one load-and-inject record and no training-peak measurement.training.activation_offloadingis not modelled, and the estimate now says so when it is set (#2331 by @ybai08).
The calibration statement moves with the record: the estimate was fitted on dense models of 3B and below on one 6 GB card, and the record now holds 42 rows, with the coefficients fitted on the first 39 (rows 40 to 42 were added for #2277, short full fine-tunes where the optimizer step sets the peak). The ratios in the record show how well the fit matches its own rows, not the error on a run outside them. Outside that range a run still warns and starts, unless its weights, context and trained-parameter state alone are more than the card holds. The record is listed under Measurement records, and the calculator, with a section "What v0.77.2 changed in the estimate", is on Pre-flight and tooling.
soup merge pins the base commit
Both of soup merge's paths, the regular merge and --low-memory, now load the base at the Hub commit recorded in the adapter's training provenance, not at the latest one (#2485 by @vaputa). In the code, the command prints Base revision: <commit> (recorded run) when a commit is recorded, and Base revision: unknown (legacy, local source or base override) when nothing is. With HF_HUB_OFFLINE set and that commit not in the cache, the merge now fails where v0.77.1 merged whatever was cached; fetch the recorded snapshot while you are online.
soup merge is what this release adds to the commands that do this: at v0.77.1 soup train already printed Base revision: ... (bound Hub commit), and the merge step of soup export and the loader behind live evaluations already loaded the base at the recorded commit. The scope stays narrow. Base-revision binding covers the transformers backend; Unsloth and MLX loads, and local or cached-path hints, are not bound yet (#1902, which stays open). A merge at a recorded Hub commit is one of the paths upstream did not exercise by hand before the release, and it was not run for this page either.
soup ship
soup ship --adapterreturns DON'T SHIP with the ruleadapter_noopfor an adapter whoselora_Btensors are all zero, before any model loads (#2434 by @ShryukGrandhi). The check is for a local adapter directory, and it needs NumPy to read the weights. It fires only when the directory holds at least one LoRA pair, everylora_Bis zero, and every standalone tensor (a saved head or bias, for instance) is zero and finite. An adapter with no LoRA pair (VeRA, for instance), or with a non-zero standalone tensor, is left to the live evaluation, and so is a Hub id, which is not checked. If the weights cannot be read (NumPy missing, for one) it prints a warning and goes on to the live evaluation, so the guard has not run.--progress jsonlreports live evaluation items after each generation chunk instead of after the whole suite. A failed chunk retries only its own items, and the scores and the event schema are unchanged.soup eval customand the forgetting probe use the same chunked path with unchanged results (#2421 by @kok-o).- Ctrl+C under
--progress jsonlexits 130, and any other unexpected exception prints anError:line and exits 1 (#2417 by @vaputa). Read in the code; upstream lists the Ctrl+C exit code among the things not exercised by hand.
--progress jsonl is still opt-in JSONL events on stderr. The verdict page is soup ship.
Text shown as typed
More than 50 pull requests, most of them by @Nari-Ab, make soup runs, soup registry, soup deploy, soup migrate, soup push, soup init, soup data and many more commands print paths, names and messages as typed: brackets and :name: text are kept, and terminal control characters are removed from the display only. The files themselves are not changed.
This is not every command, and the release's own Known Limitations say what is left:
- The
:name:emoji codes still expand in the diagnosticssoup eval compareandsoup eval leaderboardprint for a non-finite stored score, and in many other modules; the tracker is #2489. - A backslash before a
[that is not a complete markup tag is dropped when a path or name is printed bysoup env lock,soup init,soup shrink,soup data mix --apply,soup draft distillandsoup registry show, andsoup export --format "[/x]"ends in a markup error with exit 1 (#2533). soup doctor --configwith a Windows path that contains a colon can expand a:name:code in itsConfig file ... cannot be readline, and thesoup draft distill --plan-onlyYAML preview wraps at the console width (#2489, #2536).
Smaller changes
- A row Soup cannot interpret is named, not silently dropped (#1818, #2281 by @EtherealMelon).
soup data cleanwarns that rows are in no recognised format and prints the detection error before it sanitises them as plaintext, so itsNo issues foundno longer reads as a checked file.soup abcounts the rows it could not use by reason and prints them when the usable total is below the threshold.soup rewind --jsonwrites a non-finite loss, baseline or ratio asnulland the record that held it carriesnon_finite: true, so the file is strict JSON; through v0.77.1 it held bareNaNandInfinity. data.max_lengthholds again for SimPO and KTO, and in the wrappers for IPO and BCO, and a prompt is cut only when the row does not fit (#1196 by @ousamabenyounes). A row that fits is passed whole, not cut (through v0.77.1 a SimPO prompt overdata.max_length // 2tokens was cut even then). This closes a v0.77.1 limitation. For KTO and BCO, upstream's changelog says the completion is now rebuilt from the untruncated answer "so an over-length prompt no longer discards it"; it does not list this among the changes that alter a result, so it has no row in the re-run table.- Training: PPO no longer crashes at the first rollout with gradient checkpointing on, and checkpoints the policy outside 4-bit and 8-bit (#2319, #2340 by @ybai08). PPO, GRPO and online DPO stop adding an input-gradient hook per step (#2373).
soup train --gpus Nasks the confirmation once (#2250 by @CG-5228).soup train --resumenames the path it searched (#2389 by @EtherealMelon). soup bench trainmeasures 4-bit and 8-bit configs whose adapters TRL casts to bf16 (#2216 by @VanshSharmaPES).soup data lintreadsSKIPPED, notOK, for a near-duplicate check that was skipped (#2411 by @harryc295).resolve_device("auto")resolves to a real device (#2396 by @harshitthek).soup adapters auditrebuilds an MLX rsLoRA alpha asscale * sqrt(rank)(#2255 by @MbappeWU).soup whyshows a recorded zero loss or step count as0;-now means not recorded (#2344).soup data mix --applyprints eachtrain:path as a double-quoted YAML scalar, so a pasted block keeps names such ason,#dataorodd: name.jsonl(#2502).soup loop watchresets the monthly spend when the UTC month changes, andsoup loop statusshows what the next iteration will see (#2387, #2424 by @abdulwaarith0).soup doctornames GPUs hidden byCUDA_VISIBLE_DEVICES(#2206 by @Nari-Ab), and its MLX support table drops the rows that config loading refuses (#2258 by @Samearth17).- Cloud: RunPod jobs bootstrap in a virtual environment and keep bounded failure diagnostics (#2352 by @kok-o, #2256 by @Nari-Ab); Lambda install output reaches the log (#2282 by @EtherealMelon). The RunPod bootstrap has not been validated on a live Pod (#2192, #2098).
- Help text:
soup mcp serve --allow-mutatingand--allow-executenow describe what the flags do (#2268 by @TejasS1233, among other stale help text); the startup banner already readmutating tools ENABLED (plan-only)ormutating tools ENABLED (execution ENABLED)at v0.77.1. The old help text had been stale since execution arrived in v0.73.3. What the flags do has not changed. - Transformers model loading uses the supported
dtypeargument instead of the deprecatedtorch_dtype, with the same precision and quantization choices (#2257 by @Nari-Ab). - The Docker
latesttag stays on the newest stable release (#2214 by @Nari-Ab). - Two recipes, 173 in the catalog.
qwen3.5-0.8b-dpo(#2184) andqwen3.5-27b-dpo(#2445), both by @kok-o, are DPO starting configurations with inherited Qwen3.5 DPO settings: not tuned for those sizes and not measured (tracked as #275). No training result, speed or memory figure exists for either. The 27B one is a text-only starting point with LoRA and 4-bit quantization, and no card size is claimed for it. - Python API: six functions of
soup_cli.utils.profilerare removed:estimate_model_memory,estimate_trainable_params,estimate_lora_memory,estimate_optimizer_memory,estimate_activation_memoryandestimate_total(#2369 by @qwist1233-cpu). Nothing called them since #2146 in v0.77.1, so their numbers were not whatsoup profileprinted, and its output is unchanged. Usesoup profile --json; the estimate itself issoup_cli.utils.vram_estimate.estimate_vram. - Non-finite values are refused in more places, and judge JSONL rows are checked before generation (#2477, #2464, #2493, #2494 by @taljeon). A stored score that is not finite is refused by
soup eval compareandsoup eval leaderboard(exit 1); the Python functionrun_gaterefuses aregression_thresholdthat isNaN, infinite or too large for a float (ValueError: regression_threshold must be finite, #2464; the--regression-thresholdflag and the eval-gate config already had their 0.0 to 1.0 bounds, so nothing changes on the command line), andensure_judge_calibrated, also a Python API function, refuses a non-finitemin_agreementormax_bias(#2493). Neither is asoup shipcheck. A judge task refuses a row that is not an object or whosepromptis not a string, naming the file and line. The check goes row by row, so a bad row at line 500 is found after 499 generations (tracked as #2535).
Measurement records
"No gate ran for this release and the preprint is unchanged": the preprint states no number that was re-measured or corrected, and its measurements stay scoped to the Soup versions it names. What changed under benchmarks/ is records and harness files, and none of them is a v0.77.2 gate. At this tag the directory holds 55 records (53 at v0.77.1), 95 harness files (88), and the index. The full list is on Measurement records.
- A free Colab T4, uncapped (#999).
run-t4-colab-free-tier.mdgained a third session, with no process memory cap, that measured throughput withsoup bench train(credit @VanshSharmaPES, #2230, #2216). The figures, the conditions that bound them and what they do not show are on Layer streaming, and this page quotes none of them. Gradient exactness on Turing for the backward, for NF4 and at 8B is still not shown. - Resident VRAM estimate (#2049, #2277).
gate-2049-resident-vram.mdnow holds 42 rows; rows 40 to 42 were added for #2277, three short full fine-tunes. Its own limit stands: the coefficients are fitted on rows 1 to 39, so the bands show the fit and not the error on a run outside it. - The 6.56x pinning figure, clarified (#2271 by @xiemocun). Finding 9 of
gate-h100-validation.mdnow says that "asserted" there means checked and reported, not required to pass: the pinned arm of that comparison was timed while its gradients were wrong (8 of 256 gradient tensors matched the reference on all three repetitions), so the 6.56x is a defective fast arm over a correct slow one. No number in the record changes. Read it as a historical timing, not as the cost of turning pinning off under today's code. - Unsloth 4-bit weight dtypes (#2301, #2376 by @ryznren).
unsloth-4bit-weight-dtypes.mdis an observation, not a gate: on one RTX 5090 withunsloth/Meta-Llama-3.1-8B-bnb-4bitand one Unsloth version, after the load and again after LoRA injection, the input embedding and the output head are bothbfloat16. It ran a real weight load and injection and no forward or backward pass, so it is not a training peak, and the record says the activation and logits coefficients stay uncalibrated for Unsloth. - CPU bf16 Fast-LoRA (#2187, #2354 by @kok-o).
fast-lora-cpu-bf16-2026-10-29.mdis an investigation on one Ryzen 7 7840HS, CPU only: on torch 2.8.0 one seed of the bf16 MLP kernel test fails in three instruction-path modes, and on torch 2.14.1 all twelve seeds pass, but the seeded input changes between torch versions, so the record does not establish that the kernel's numerics improved. No kernel or criterion change is proposed, and it claims no CUDA, speed, memory or quality result.training.fast_lorakeeps no speedup claim. stream_loss_chunkhas no record inbenchmarks/, only the harness that measures it. Upstream measured it once (see The VRAM estimate prices some runs differently); the table lives in its performance documentation and on #1693, so this site prints no figure from it.
Docker and install
pip install -U soup-cli
soup --versionWith pipx, pipx upgrade soup-cli. There is no dependency change in this release (the only line pyproject.toml changes is the version), and the Python range is unchanged at >=3.11,<3.14, which is 3.11 to 3.13. Python 3.10 stays on v0.76.x (pip install "soup-cli[train]<0.77"), and AWQ and GPTQ export were removed in v0.77.0. The Dockerfile is unchanged since v0.77.1.
The image is ghcr.io/makazhanalpamys/soup:0.77.2, also tagged latest, and latest stays on the newest stable release (#2214). The host driver must support CUDA 12.8.
docker pull ghcr.io/makazhanalpamys/soup:0.77.2
docker run --gpus all -v "$(pwd)":/workspace ghcr.io/makazhanalpamys/soup:0.77.2 train --config soup.yaml --yesKeep the --yes: docker run without -t gives soup train no terminal on stdin, and since v0.77.0 it exits 3 in that case unless --yes is passed. The image was not pulled for this page.
Credits
153 of the 159 pull requests in this release came from 30 people outside the maintainer, in the release body's count. Grouped by person, in the release body's order of merged pull requests, with what each found or built. The maintainer's pull requests carry no credit line here.
| Who | PRs | What |
|---|---|---|
| Nariman Abdikarim (@Nari-Ab) | 69 | Text shown as typed in more than 50 pull requests; shell completion scripts restored (#2283); --minillm-on-policy on soup plan and soup apply (#2315); dtype for Transformers loads (#2257); Windows paths in soup adapters bisect --eval-command (#2314); named config read errors in soup plan and soup apply (#2323); GPUs hidden by CUDA_VISIBLE_DEVICES in soup doctor (#2206); Docker latest on the newest stable release (#2214); bounded RunPod failure diagnostics (#2256); JSON diagnostics on stderr for soup profile (#2398) and soup cost (#2345); recorded zero metrics in soup why (#2344); Click-safe test runners (#2189, #2334) |
| Nurkhan Esenbek (@kok-o) | 16 | Output-over-input refusals for the remaining soup data commands and soup data ingest (#2274, #2349); soup data evolve and gen-magpie keep their output when no row is produced and a provider call fails (#2348); soup data filter --perplexity fails without its dependencies (#2377); soup data dedup --field on an absent key (#2336); thumbs-up pairs that differ (#2495); live soup ship --progress jsonl items per chunk (#2421); the RunPod bootstrap in a virtual environment (#2352); corrected DPO demo pairs (#2335); the recipes qwen3.5-0.8b-dpo and qwen3.5-27b-dpo (#2184, #2445); benchmark, test and docs records (#2172, #2183, #2354, #2413, #2273) |
| Yang Bai (@ybai08) | 11 | The VRAM pre-flight charges a stream_loss_chunk step's last-layer recompute (#2290), prices a sharded run per card (#2313) and a PRM step's logits (#2337, #2372), and says it does not model activation offloading (#2331); PPO gradient checkpointing (#2319, #2340); one input-gradient hook across PPO, GRPO and online DPO steps (#2373); MLX GRPO on pre-quantized 4-bit bases (#2386); a streamed DeepSeek-V3 test for DPO, ORPO, SimPO and KTO (#2291); MLX TF32 default off in the native tests (#2391) |
| Abdulwaarith Zakariyya (@abdulwaarith0) | 8 | soup profile --model (#2285); task: sft refuses the four settings a path never reads (#2278); the optimizer step as its own phase of the VRAM peak (#2338); finish_reason: "length" replies (#2380); the monthly spend reset in soup loop watch and the state shown by soup loop status (#2387, #2424); the Fast-LoRA timing harness (#2287); the Windows CI bf16 skips (#2346) |
| Tal (@taljeon) | 5 | Eval batches that mutate their input (#2463), a non-finite regression tolerance (#2464), non-finite stored scores (#2477), non-finite judge recheck limits (#2493) and judge JSONL rows checked before generation (#2494) |
| EtherealMelon (@EtherealMelon) | 4 | Rows Soup cannot interpret are named instead of dropped (#2281); Lambda install output kept in the log (#2282); soup train --resume names the path it searched (#2389); three tests no longer depend on the machine (#2318) |
| Faisal Fayaz (@Faisal-Fayaz) | 4 | soup eval quant-check exits 3 on an unparseable --tasks file (#2185); the #792 CUDA rows rewritten against the 2x float64 rule (#2188); NF4 streaming and o_proj Fast-LoRA coverage (#2328); the unlisted MLX tests run in mlx-smoke (#2342) |
| vaputa (@vaputa) | 4 | MLX task: preference (#2333); soup ship --progress jsonl errors and exit 130 on Ctrl+C (#2417); MLX Adafactor with the configured learning rate and schedule (#2484); soup merge at the recorded base revision (#2485) |
| Ryzn (@ryznren) | 3 | Unsloth targets and 4-bit weight dtypes in the VRAM estimate (#2376); duplicate README stamps and banners rejected by a test (#2316); GRPO judge reason length boundaries covered (#2317) |
| Aditya Pratap Singh (@akkupratap323) | 2 | soup distill-prompt refuses traces with no prompt instead of emptying its output (#2209); the strip-before-escape order for bidi and invisible format characters pinned (#2350) |
| Sharvin (@Guts1005) | 2 | soup registry push refuses runs that are not completed (#2248); soup ci init --python checks the supported range (#2249) |
| Harry C (@harryc295) | 2 | A skipped near-duplicate check in soup data lint reads SKIPPED, not OK (#2411); CONTRIBUTING names scripts/sync_recipe_count.py (#2394) |
| Harshit Sharma (@harshitthek) | 2 | resolve_device("auto") resolves to a real device (#2396); CPU GRPO numerical probes (#2364) |
| WU Leizhi (@MbappeWU) | 2 | The MLX rsLoRA alpha rebuilt as scale * sqrt(rank) in soup adapters audit (#2255); /v1/messages applies temperature and top_p (#2388) |
| ShryukGrandhi (@ShryukGrandhi) | 2 | soup adapters arithmetic and merge refuse an output that names an input adapter (#2379); soup ship --adapter refuses an adapter whose lora_B tensors are all zero (#2434) |
| shubham-dev-stack (@Shubham-dev-stack) | 2 | An unreachable minillm_enabled check removed (#2422); the MLX test scan memoized (#2504) |
| Vansh Sharma (@VanshSharmaPES) | 2 | soup bench train measures 4-bit configs whose adapters TRL casts to bf16 (#2216); the T4 forecast bracket measured against a real tokens-per-second figure (#2230) |
| King's MAN (@CG-5228) | 1 | soup train --gpus N asks the confirmation once and passes --yes to the child ranks (#2250) |
| Columbus Labs (@ColumbusLabs) | 1 | soup quantize --to awq and gptq name their v0.77.0 removal, and a --to value is shown as typed (#2217) |
| Mayuri (@mayuriphad) | 1 | MLX DPO --resume refuses an adapter of another task or base (#2321) |
| Matt Van Horn (@mvanhorn) | 1 | Rich markup in soup data paths and flags no longer raises MarkupError (#1882) |
| Sohaib Kamash (@Nick-800) | 1 | soup infer --cuda-graphs inspects a local adapter's recorded base before the load (#1942) |
| Ben Younes (@ousamabenyounes) | 1 | data.max_length enforced across the SimPO, KTO, IPO and BCO wrappers (#1196) |
| qwist1233-cpu (@qwist1233-cpu) | 1 | The unused estimate functions of utils/profiler.py removed (#2369) |
| Samarth Mukhija (@Samearth17) | 1 | soup doctor --config drops MLX support rows that config loading refuses (#2258) |
| Shivansh Shukla (@ShivanshShukla) | 1 | The Aider benchmark container is removed on timeout and Ctrl+C (#2280) |
| Baktiyar Ablaikhan (@sronters) | 1 | k-bit logits memory overhead pinned by a test (#2351) |
| Tektrix (@Tektrix) | 1 | The mypy errors added in v0.77.1 annotated and narrowed (#2320) |
| Tejas (@TejasS1233) | 1 | Stale v0.77.0 help text corrected (#2268) |
| xiemocun (@xiemocun) | 1 | The pinned arm in the 6.56x comparison of benchmarks/gate-h100-validation.md marked as the wrong one (#2271) |
Reports that led to fixes in this release, as the release body thanks them: @Batyrkhantzy111 (#2359, #2360) and @abdulwaarith0 (#2288).
Known limitations
New in this release, in upstream's terms:
- MLX coverage. On Apple Silicon,
task: preference(DPO, ORPO and SimPO only; IPO, BCO andpreference_loss_weightsblends stay refused) and MLX GRPO on 4-bitmlx-communitybases are covered only by the hosted macOS MLX test job. MLX GRPO still refuses--resume, 8-bit and mixed-bit bases,mxfp4,mxfp8andnvfp4, quantized mixture-of-experts and BitNet layers,lora.r: 0, custom or ensemblereward_fnsettings and non-text modalities. Themlxextra's declared floor is tracked in #2471. - The VRAM estimate. The per-card estimate of a run sharded across more than one process is fitted on single-card runs, so it warns instead of refusing, unless the per-card floor alone (weights, context and the state of the trained parameters) is larger than the card, which still refuses (upstream's own wording is "warns and never refuses"; the code decides). The
stream_loss_chunkrecompute charge is measured on one model and one card and does not cover fusedgate_up, mixture-of-experts layers or LoRA on MLP modules.training.activation_offloadingis not modelled (the estimate says so when it is set). The Unsloth pricing rests on one load-and-inject record with no training-peak measurement. - KTO and BCO on trl 0.29. A row whose prompt is cut to
max_length // 2loses its BOS token (#2487), and the KTO length cap reads the whole answer columns into memory (#2486). soup adapters bisect --eval-commandon Windows splits the template without POSIX quoting: write"{ckpt}"in double quotes (#2488).- Text shown as typed, what is left: the
:name:emoji codes, a backslash before a[,soup export --format "[/x]", a Windows path with a colon insoup doctor --config, and thesoup draft distill --plan-onlypreview (#2489, #2533, #2536); details under Text shown as typed. soup ci init --pythonstill accepts03.11and3.011and writes them into the workflow as typed (#2534).- A judge task in the eval gate checks its JSONL row by row, so a bad row at line 500 is found after 499 generations (the check arrived in #2494) (#2535).
- Partial fixes whose issues stay open. Only the MLX Adafactor slice of the settings a backend never applies is fixed, and the rest stays open (#1811).
soup mergenow loads the base at the recorded commit, as the merge step ofsoup exportand live evaluations already did; Unsloth and MLX loads and local or cached-path hints are still not bound, and an offline merge now needs that snapshot cached (#1902).qwen3.5-0.8b-dpoandqwen3.5-27b-dpouse inherited settings and are not tuned or measured (#275). The RunPod virtual-environment bootstrap has not been validated on a live Pod (#2192, #2098). - Not exercised by hand before this release, in upstream's list: any GPU path, MLX itself, the
Origincheck of the vLLM, SGLang and MII servers, the RunPod bootstrap, the Ctrl+C exit code 130 ofsoup ship --progress jsonl, and a merge at a recorded Hub commit. The pre-release smoke run was on CPU.
Carried over from v0.77.1, still open:
training.fast_lorahas no speedup claim and no end-to-end claim; its correctness half is open (#792).training.stream_recomputeis SFT only;autoworks on the RAM tier only, gains nothing on a read-bound disk tier, and at a high VRAM fill it is slower.- Kimi K2 admission is verified on CPU with a tiny K2-shaped checkpoint only; nothing has been trained on K2. Its tokenizer needs
--trust-remote-codeandpip install tiktoken. Resident K2 loads are refused at load, and Kimi-K2-Thinking and Kimi-K2-Instruct-0905 have no supported route yet (#2104). stream_expert_layout: per_expertcovers the DeepSeek-V3 family only and costs step time; a non-NVMe cache volume is warned about.two_read_expertshas no hardware speed record yet (#2170).- After upgrading, the first pinned layer-streamed run re-shards into a new
<slug>@<commit>cache slot; the old slot stays on disk and nothing names or removes it (#2196). - The RunPod controller has not been run against a real RunPod account (#2098); local input files are not uploaded.
soup export --format ggufstill clones llama.cppb5270, which cannot convert Qwen3.5; it refuses before cloning and names--llama-cpp <dir>(#1911).lora.rank_patternandalpha_patternare refused on MLX and Unsloth until those trainers apply them (MLX: #1996).freeze_layersandfreeze_ratiowith LoRA need a list oflora.target_modules; a regex,all-linear,lora.target_parametersand VeRA are refused when the model is built (#2201).soup merge --low-memoryrefuses adapters it cannot reproduce exactly (DoRA,modules_to_save, rank or alpha patterns, embedding adapters) and names the regular merge instead.soup serve's/v1/tools/pythonis unavailable (501) on Windows and on Linux with Python 3.11, as/v1/tools/bashalready was.- Native MLX inference covers text LoRA adapters only;
soup chaton MLX is open (#1986, #2123). - Still open from v0.76.0: fields that reach no consumer outside the staged table (#1814),
soup doctor --configcoverage (#1815) and config-error wording (#1816). - Layer streaming remains BETA and opt-in;
--cuda-graphsremains experimental.
Fixed since v0.77.1 and no longer limiting, in the release body's list: SimPO and KTO rows past data.max_length (#1185), the unmeasured stream_loss_chunk peak (#1693), the soup data commands not yet checked for output aliases (#2226), soup plan and soup apply without --minillm-on-policy (#2303), the refusal of MLX task: preference for DPO, ORPO and SimPO (#2202), the dense-only MLX GRPO baseline (#1976) and rows of an unknown shape that were named nowhere (#1818). The v0.77.1 page's own list is on its page.
What was checked, and what was not
Checked for this page, on the v0.77.2 binary (a light install with no Torch, MLX or GPU, on tiny local inputs, with the network off and the home directory redirected): the soup serve token refusal for a short value, a value with spaces on a wildcard bind and a short SOUP_TOOL_AUTH_TOKEN; soup doctor --config on the task: sft refusals for backend: unsloth, backend: mlx and data.format: raft, with soup train --dry-run --yes and soup plan on the first, and a false value that loads; soup plan and soup apply on malformed YAML, on a missing file and on a distill config with and without --minillm-on-policy; soup eval quant-check on a .txt and an invalid-JSON tasks file; soup distill-prompt on traces with no prompt; soup quantize --to "[/]" and soup ci init --python 3.10; soup adapters bisect on this binary and on v0.77.1 for a program that {ckpt} completes to a missing file; the output refusals of soup data persona-mix, ingest, sample, lint, and of soup adapters arithmetic and merge; soup profile --model on a toy config.json, on a directory without one and with --config; soup doctor --config on MLX task: preference (dpo loads, ipo is refused) and task: grpo configs (a 4-bit base loads, quantization: 8bit is refused), and soup train --dry-run --yes on the 4-bit GRPO one; soup mcp serve --help; and soup data lint on a pairs file with identical rows. Test files go from 1106 to 1220 and the catalog from 171 to 173 recipes, counted at the tag.
Read in the code and not run: the MLX training paths (no Apple Silicon here); the Origin check of the vLLM, SGLang and MII servers; the /v1/tools/python and /v1/tools/bash replies; the Ctrl+C exit code of soup ship --progress jsonl and its Error: line; the success path of soup ship --adapter and adapter_noop; the recorded base revision in soup merge; the soup rewind --json strict output; the monthly spend reset; the cloud bootstraps; and every term of the VRAM estimate. The Docker image was not pulled and no Hub model was read.
Not measured, and not claimed: an MLX speed, memory, time or accuracy figure; the estimate's error on any run outside the rows it was fitted on; the stream_loss_chunk table; any figure for the two new DPO recipes; and the Origin check on a running vLLM, SGLang or MII server.
See also
- v0.77.1: data commands keep their input: the release before this one, whose unchecked
soup datacommands andsoup plangap this release closes. - v0.77.0: what v0.76 warned about is now refused: the refusals this release extends to four more
task: sftsettings. - Settings Soup refuses at load, Configuration and Pre-flight and tooling: the load checks,
soup planand the hardware-fit calculator. - Exit codes: the full table.
- Inference server and MCP server: the token rule, the
Origincheck and the execution flags. - Apple Silicon MLX backend, soup ship, Experiment tracking and Adapter lifecycle: preference and GRPO on MLX, the verdict command,
soup profileandsoup adapters bisect. - Data tools and Recipes: the
soup datacommands and the catalog. - Layer streaming and Measurement records: the T4 session and the records listed above.
- What's new across v0.71 to v0.77.
- The upstream changelog at the tag and the comparison with v0.77.1.
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.