What's New
v0.66.0 — Post-train X-rays: 5 surfaces extend soup diagnose from 6 failure modes to 10. Mechanistic interpretability shipped CLI-first because hosted vendors structurally can't (SaaS unit economics break on O(N) influence functions). Closes v0.57 #171 — the NotImplementedError blame stub is lifted with a DataInf-style influence-function approximation.
soup probe sae-diff <sae> <pre.json> <post.json>— Sparse-Autoencoder feature attribution. Pure-numpy:ReLU(activations @ W_enc + b_enc)→ per-feature mean post-pre delta → top-K + L2 drift. ClosedHF_HUB_ALLOWLISTover 8 known SAE families (Gemma Scope / Pythia / SAE-Lens Llama / OpenAI GPT-2-small). Containment + O_NOFOLLOW open + 64-tensor cap.soup adapters blame --top-k 50— Live DataInf-style runner closes #171. Per-row score =cos(grad_row, grad_probe) × |grad_row|. Operator suppliesprobe_fnor falls back to a deterministic synthetic probe so the surface always returns a realBlameResult(never raises).soup probe sleeper <base> [--evidence ev.json]— Calibrated linear probe per base. 6 bundled bases (Llama-2/3 8B, Mistral-7B, Qwen2-7B, Gemma-2 2B/9B). OK / MINOR / MAJOR at 1% / 5% defection rate. Exit 2 on MAJOR for CI gating.soup probe interference <losses.json>— Pairwise N×N adapter interference matrix. OK / MINOR / MAJOR at 5% / 20% bands. Surfaces which adapter pairs you cannot deploy together viasoup serve --adapters. MAJOR worst-pair exits 2.soup probe pack <base>— Manifest assembler. Per-base probe bundle (sleeper / sae / truth / harm; last two reserved for v0.66.x).
Install / Upgrade
pip install --upgrade soup-cliSecurity
- TOCTOU defence via O_NOFOLLOW —
load_sae_weightsand_count_dataset_rowsnow probe-open withos.O_NOFOLLOWso a symlink swap between the containment check and the read raises immediately rather than silently following. - Process-independent deterministic seeds —
_probe_weights(sleeper) and_default_synthetic_probe(blame) replace process-saltedhash()withhashlib.sha256(key).hexdigest()[:16](64-bit seed). CI reproducibility now works regardless ofPYTHONHASHSEED. - Rich-markup escape on operator-controlled adapter / verdict / description / layer strings before embedding into markdown render. A crafted adapter name like
[link=evil]click[/]cannot inject Rich markup. - TypeError on bool/non-str verdict —
SleeperProbeResultandInterferenceCelltype-check verdict before the membership check, soverdict=Trueraises TypeError (was: misleading ValueError). - Non-numeric loss rejection in
soup probe interferenceCLI — JSONtrue/falsedeserialise to Pythonbool(subclass ofint), explicit guard fires first. - DoS caps: 10M-row hard reject on dataset row count (no silent truncation);
_DEFAULT_SYNTH_PROBE_CAP=100_000so a 10M-row default-probe never allocates 1.28 GB;_MAX_ADAPTERS=16(256-cell matrix ceiling);_MAX_DESCRIPTION_LEN=4096onProbeEntry; safetensorslen(keys) > 64pre-check before tensor materialization. - Frozen dataclasses everywhere +
__post_init__validation; tests assertpytest.raises(FrozenInstanceError)(replaces generic(AttributeError, Exception)which would silently pass on dropped frozen flag).
Known Limitations
- Bundled sleeper-probe weights are deterministic but SYNTHETIC —
_probe_weightsderives from a SHA-256 seed so CI is reproducible, but the weights are NOT real Anthropic-style calibrated probes. Real weights ship in v0.66.x. - No live HF Hub auto-download for SAE / probe weights — operators must already have the safetensors file locally; the allowlist is the recorded-provenance closed set, not a fetcher.
truth+harmprobe kinds reserved —PROBE_KINDSaccepts them today as a forward-compat surface, but no runtime utility consumes them yet.run_blamesynthetic-probe default is offline-only — scores fromprobe_fn=Noneare not meaningful (seeded RNG). Real workflows pass a realprobe_fnreturning gradients from a held-out probe set.- Interference matrix uses operator-supplied losses — Soup does not measure them automatically; operators run
soup serve --adapters A,B(v0.22), capture per-prompt losses, and feed the JSON. - SAE feature diff uses operator-supplied activations — operators capture hidden states externally via
model.forwardhooks.
Test count
10577 → 10836 (+259 net across 7 new test files).