Tags: ghzhost/Soup
Tags
fix(tests): read the Qwen4 credit from wherever it currently lives test_qwen4_gate_record_and_changelog_are_discoverable_and_credited pinned changelog.d/0.73.3/603.added.md by absolute path. Per-PR fragments are consumed at release time by design (MakazhanAlpamys#487/MakazhanAlpamys#490), so assembling v0.74.0 deleted the file the test opened and turned all nine cells red with a FileNotFoundError -- an assembly that is supposed to happen, failing a test whose subject is the credit rather than where the credit is stored. It now reads CHANGELOG.md and any surviving 603 fragment and requires the credit in whichever currently carries it, so it holds both before and after a release. The gate-record assertion is unchanged. Both assertions also gained failure messages naming the consequence, and the "neither source is readable" case is asserted rather than silently passing on an empty list. Mutation-verified: removing `by @Amix29` from the assembled CHANGELOG.md entry fails the test; restoring it passes. So the repair did not turn the test vacuous, which is the obvious way to "fix" a red path-pinned test. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
docs(contributors): Darsh's eighth merge (MakazhanAlpamys#400) MakazhanAlpamys#400 merged on 2026-08-15, after the v0.73.2 tag, and was never counted: the block listed seven PRs and the docs(contributors) commit stopped at MakazhanAlpamys#393. Found while reconstructing attribution for the v0.73.3 release notes. It is a one-line correction to somebody else's credit in the CHANGELOG, which makes leaving it uncredited a particularly poor outcome.
fix(test): strip ANSI before asserting on CLI output (v0.73.2) CI red on all nine test cells, one test: test_the_flag_exists_and_is_documented. Rich/Typer emits PER-CHARACTER ANSI escapes on a colour-capable runner, so `--noise-floor` arrives as \x1b[1;36m-\x1b[0m\x1b[1;36m-noise\x1b[0m\x1b[1;36m-floor\x1b[0m and `"--noise-floor" in result.output` cannot match. Windows passes because Rich auto-disables colour there, which is exactly why the local run was green -- the same platform asymmetry that has now turned this project's CI red four times (test_auto_tuning.py documents the previous one and carries the same helper). Adds _plain() -- ANSI-strip plus whitespace-collapse, since Rich also WRAPS -- and routes EVERY CLI-output assertion in the file through it, not just the one that failed. Verified under FORCE_COLOR=1 with a TERM set, which reproduces the escape sequences locally; 175 pass in both colour modes. Two guards so this does not come back a fifth time: - the helper is pinned against the exact byte sequence from the failing run, with a control that it does not invent matches (a helper that collapsed everything would make every assertion in the file vacuous); - a SCAN of this file's own source, not a hand-written list, fails on any surviving `in result.output` / bare `readouterr().out` assertion. It considers only lines that actually start with `assert`, because the guard's own prose and string literals mention the pattern. Also corrects a flaw in this release's own security test. It asserted "\x1b not in the panel", which conflates Rich's legitimate SGR styling with injection -- it would pass on Windows and fail on a colour-capable runner while testing the wrong property. It now asserts the specific hostile sequences are gone (OSC title-set, BEL, CSI clear-screen), with a control that the fixture still contains them.
fix(mlx): write the RESOLVED target modules, not `auto` (MakazhanAlpa… …mys#392) Reported by @armanbot-jpg with a root cause and, more usefully, a control: hand-editing `keys` in the saved adapter_config.json makes the very same adapters.safetensors produce the tuned behaviour. That is what proves the weights were fine and the config was not. _apply_lora resolved `target_modules: auto` into a LOCAL variable and trained Q/V; the writer serialised lora_cfg.target_modules, i.e. the raw "auto". On load, linear_to_lora_layers(..., {"keys": ["auto"]}) matches no module and load_weights(strict=False) drops all 96 LoRA tensors silently, so generation with the adapter is bit-identical to the base model. "auto" is the schema DEFAULT, so this was every MLX run that did not name its modules by hand -- and the adapter_config.json exists specifically to promise the output dir loads with mlx_lm.load(..., adapter_path=...). Third time in this project a healthy loss curve has shipped a dead artifact (v0.72.0's .inner. keys, MakazhanAlpamys#362's full-fine-tune adapter, this). The loss curve structurally cannot see it; only the artifact can. So the repair is one shared resolve_mlx_target_keys() rather than teaching the writer to resolve too -- two copies of "which modules did we train?" is how they drifted. Tests first: 10 red before, 10 green after. Two are controls -- the resolved value must never contain "auto" whatever the input, and rank/scale/dropout must still come from the config, so a repair that hardcoded the whole block would fail rather than pass. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
release: v0.73.0 — three days on somebody else's hardware The only CODE in this release is MakazhanAlpamys#358. Everything else shipping here already landed on main, reviewed and TDD'd at its own commit, over the 5-9 August 8xH100 session: the MakazhanAlpamys#331 silent wrong-gradient repair, four backends that had never actually run, the vLLM rewrite, training.seed (MakazhanAlpamys#341), full fine-tuning as lora.r=0 (MakazhanAlpamys#340), and the trl cap raised to <0.29 behind a capability probe (MakazhanAlpamys#326). MakazhanAlpamys#358 — requires-python was ">=3.10" with no ceiling while CI tests 3.10/3.11/3.12 and nothing above. On 3.13+ pip resolved torch wheels nobody here has run, and the failure is not a Soup error message: it is a loader crash inside c10.dll / libc10.so before any Soup code executes, so the user has nothing to act on. Set to ">=3.10,<3.13" — NOT the <3.14 an external report suggested, because 3.13 is equally untested here. tests/test_requires_python_bound.py DERIVES the bound from the CI matrix rather than hardcoding it, because a floating declaration and a fixed matrix drifting apart is the failure it exists to catch: widening the matrix and widening the bound must happen together. RED first — 4 failed / 4 passed against ">=3.10", and the 4 that passed are the controls (a missing ceiling must raise; a ceiling that lags the matrix must not read as agreement). WHY A MINOR AND NOT v0.72.5. 28 feat/fix commits since v0.72.4, two of them feat, adding capabilities that did not exist. A patch in this project means something like v0.71.37 (pip install quoting); v0.72.5 in the CHANGELOG would hide half the week. The v0.72.5 number was never used — the hardware-gated reference benchmark that held it is renumbered v0.73.1 and stays infra-blocked. STEP 5 SKIPPED DELIBERATELY, not silently. The five ECC review agents are not run for this commit: the change is one line of packaging metadata plus its test, not src/ code, which the checklist's own CI/docs-hotfix clause allows. Every src/ change in this release went through the full review + TDD cycle when it landed. STEP 6 was run in full: `soup version` prints 0.73.0 against both files, `soup --help` loads, the BUILT WHEEL carries `Requires-Python: <3.13,>=3.10`, and the specifier refuses 3.9 / 3.13 / 3.14 while accepting 3.10-3.12. The two new capabilities were exercised through load_config_from_string as well — seed set and unset-is-None, lora.r=0 parses, r=-5 refused naming the bound. Hard gate met: full local pytest on Windows / py3.10 / torch 2.5.1 / trl 0.19.1 — 17344 passed, 135 skipped, exit 0, coverage 81.97% in 17m13s. ruff clean. STEP 13a — benchmarks/gate-h100-validation.md was already committed and indexed; verified here rather than re-added. Its filename deliberately does NOT follow gate-vX.Y.Z-<slug>.md: it is an external-hardware validation spanning the whole series, not the gate for one version, so it is not renamed. One stale row IS fixed: the index still said the 72B backward was "not re-run" after STEP 14 re-gated it at 320/320. STEP 13b — no MEASURED number the preprint states changes (its 8B NF4 config sits at 105 MiB/layer, below the 163.8-171.5 MiB MakazhanAlpamys#331 boundary, and survives a 50-backward soak at worst_abs 0.0). The SCOPE changes, and Zenodo v2 is where that lands. One caveat is now published rather than left implicit: the 119.6 tok/s laptop figure predates the MakazhanAlpamys#331 repair and has not been re-run on repaired code (the repair cost -4.8% at 32B), so it ships labelled pre-repair. An H100 cannot substitute — the method is transfer-bound, which is precisely what the H100 run established. Also renamed tests/test_v07205.py -> tests/test_v07300.py. It covers the MakazhanAlpamys#331 repair, which ships HERE; the version it was named for no longer exists. Docs: CHANGELOG gets the whole week, not just MakazhanAlpamys#358. README's What's New block is REPLACED (v0.72.4 rolled into a details block, the oldest one dropped). Python badge 3.10+ -> 3.10-3.12. SECURITY supported window 0.72.x -> 0.73.x. Test counts 17068 -> 17344 across CLAUDE.md (3 places) and CONTRIBUTING (2). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
fix(tests): actually apply the two skips — the previous commit applie… …d neither cb44d9e claimed three fixes and shipped one. The scripted edit aborted on its first assertion and wrote nothing; I then applied only the bf16 fix by hand and did not re-check the other two. Neither local run could catch it: the CUDA-gated test passes on this box's CPU under torch 2.5.1 (it only fails on CI's 2.13), and there is no MPS device here, so both "fixed" tests looked green locally while carrying no marker at all. Applied and verified two ways this time: - structurally, by parsing the file and asserting each decorator is attached to the test it belongs to; - behaviourally, by running with CUDA hidden and confirming the gated test reports SKIPPED rather than silently executing. 62 passed / 5 skipped with CUDA hidden; 67 passed on CUDA.
fix(tests): NF4 fixtures used a degenerate hidden_size that CPU bitsa… …ndbytes rejects Two NF4 bit-exactness tests failed on all nine CI jobs while passing locally. Root cause reproduced directly: bitsandbytes' CPU 4-bit forward calls `_convert_weight_packed_for_cpu`, which reshapes absmax to [rows, blocks_per_row]. At hidden_size 32 a weight has 32*32/64 = 16 absmax blocks for 32 rows, so blocks_per_row floors to ZERO and it raises "shape '[32, 0]' is invalid for input of size 16" — the exact CI error. Verified 32 raises while 64 and 128 do not. A CUDA build never calls that function, which is why a GPU development box cannot see this and every CPU-only runner fails. The fixtures move to hidden_size 64 with the reason recorded in-place so they are not shrunk back. Fixture sizing only: nothing about the product changed, and both suites still assert bit-exactness.
test(stream): skip the two NF4 training-step tests when MPS is the ac… …celerator CI was red on macOS only (3/11 jobs); ubuntu and windows were green across 3.10/3.11/3.12, as were lint and type-check. Cause is not bitsandbytes availability but device disagreement: on an Apple-Silicon runner with no CUDA, TrainingArguments picks `mps`, while this suite builds the streamed model on `cpu`. The batch is then moved to MPS and the step raises "Placeholder storage has not been allocated on MPS device!". Only the two tests that actually call trainer.train() were affected; test_setup_builds_a_real_trl_trainer_under_nf4 passed, because building the trainer never touches a device. v0.72.0 hit exactly this and guards test_one_training_step_actually_runs the same way; this mirrors that helper rather than inventing a second one. NF4 streaming is measured on CUDA and CPU only, and bitsandbytes' 4-bit kernels have no MPS support, so skipping is the honest outcome — not a claim that it works there. Verified on the CUDA dev box: 88 passed, zero skipped, i.e. the guard does not over-skip where the tests are meaningful.
fix(train): streamed adapters were saved unloadable (v0.72.1) v0.72.0's layer-streaming wrapper holds the real decoder layer as a child named `inner`, so every saved LoRA adapter key carried an `.inner.` segment. Such a file reloads as ZERO tensors into any normal model: soup merge, soup serve, soup chat and PeftModel.from_pretrained all returned the untuned base while PEFT emitted only a UserWarning. Training was correct; only the artifact was inert. StreamedDecoderLayer.state_dict() now delegates to the wrapped layer at the wrapper's own prefix, so every artifact path -- the final trainer.save_model(), each save_steps checkpoint, and therefore soup adapters, the Registry, merge and serve -- becomes canonical at once. Serialisation-only by design: the forward path is untouched, so v0.72.0's bit-exactness gates remain valid without being re-earned. Also fixes --hf-resume bypassing the streaming resume refusal. The guard tested only --resume, while --hf-resume reaches resume_from through another branch. Pre-fix that combination matched keys by accident; post-fix it would have matched nothing and silently continued training with a freshly initialised adapter -- i.e. the adapter-key fix alone would have made that one path worse. Roadmap renumbered (this release was inserted ahead of NF4): every "lands in vX.Y.Z" refusal corrected -- NF4 v0.72.2, disk tier / more architectures / larger batches / gradient accumulation / checkpoint-resume v0.72.3, preference losses v0.72.4. Found by the v0.72.2 NF4 gate, not by the 159 v0.72.0 tests -- none of them saved an adapter and loaded it back. The new regression test does exactly that, by count, by name and by value, with a negative control that re-mangles the keys and asserts the reload yields zeros (0-of-N loading raises nothing, so a green round-trip without the control proves nothing). Tests: +17 in tests/test_v07201.py (16735 -> 16752). Full suite: 16623 passed, 129 skipped, 4 deselected. Note for maintainers: do not edit soup_cli/__init__.py while a suite is in flight -- a mid-run version bump made test_cli_subprocess::test_version fail spuriously (the subprocess and the imported constant disagreed).
docs(v0.72.0): list layer streaming in the README docs index and comm… …ands reference Two spots the release missed: the README's own docs-index row for Performance & quantization (docs/README.md's equivalent row was already updated), and docs/commands.md, which lists config-driven training features in the same style as LISA and Spectrum. Docs-only — no version bump.
PreviousNext