Skip to content

tools: clef_adapter.py rebuilds a Clef runtime adapter from the merged release and its stock base - #1366

Closed
gilby wants to merge 10 commits into
ashhart:mainfrom
gilby:native-clef-extract
Closed

gilby wants to merge 10 commits into
ashhart:mainfrom
gilby:native-clef-extract

Conversation

@gilby

@gilby gilby commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Part of #1360. Stacked on #1364 (Clef's runtime adapter); the last commit is new here.

--clef DIR loads Clef as a LoRA on the base checkpoint, but Cloudflare publishes Clef only as merged weights. This adds the tool that rebuilds the adapter directory from public weights, so nobody has to trust a third-party upload (ours is unigilby/Qwen3.8-27B-clef-adapter).

python tools/clef_adapter.py --merged Cloudflare-clef/ --base Qwen3.8-27B/ --out clef-adapter/ \
    [--merged-revision SHA] [--base-revision SHA] [--rank 256] [--threads N] [--check OTHER_ADAPTER_DIR]
  • Finds the tensors that differ between the merge and the base; every one must be a 2-D projection weight (a changed norm or embedding is refused). Base-only tensors, such as an MTP head the merge dropped, are ignored.
  • Factors each difference exactly (fp64 eigendecomposition of the smaller Gram matrix) at rank min(256, out, in): bf16 A with orthonormal rows, B with the scale folded in, keys layers.{i}.{module}.lora_A / .lora_B.
  • Copies Cloudflare's joint head files and LICENSE, writes lm_head_rows.safetensors from the base's output embedding (asserted identical in the merge), manifest.json with both revisions and every module's shape and rank, extract_report.json (each projection's residual against the bf16 rounding floor of the merge) and SHA256SUMS.json. --check compares the reconstructed deltas B A with another adapter directory.
  • CPU only, one projection in memory at a time; needs torch and safetensors, like the other converters in tools/. Its tests need them too and are skipped without them.
  • A wide delta keeps only singular values above s[0] * 1e-7, an all-zero delta and any non-finite factor are refused, and a merge without the output embedding is named as such.

Receipts

Cloudflare/clef @ 0b331204 against Qwen/Qwen3.8-27B @ 1d4bf0f2 (bf16), 32 CPU threads, 773 s:

  • 186 projections differ (every linear in layers 40–63; the 15 MTP tensors only the base holds are ignored).
  • Residual 0.98–1.03× the bf16 rounding floor of the merge on every rank-256 projection (worst layers.40.mlp.gate_proj: 1.18% vs a 1.14% floor).
  • Against the published adapter: 185 of 186 factor pairs bit-identical, the last equal up to the sign of its SVD components (A and B negated together); every reconstructed delta identical (--check: worst relative difference 0.0). lm_head_rows tensor bit-identical; joint head files and LICENSE byte-identical; manifest modules, shapes and ranks identical.
  • The published adapter itself reproduces the merged release to mean KL 3.9e-7 / top choice 100% (fp32, 39 records), and serves Cloudflare's 60-record gate at KL 0.0085 / 95.87% on native TensorFold (qwen3_5 cuda: Clef's adapter at runtime on the 27B's decision passes; chat never runs with it #1364).
  • tools/tests/test_clef_adapter.py (skips without torch): a synthetic low-rank delta is recovered with orthonormal A, the manifest and head files are written, a changed norm is refused, a rank-deficient wide delta keeps its rank with finite factors, and a merge without the embedding is named. 4/4 pass.

gilby added 4 commits October 26, 2026 08:50
…ost, equal to its Python within fp32 rounding
…ine's final rows; the Qwen 27B CUDA family gives them
… block, and its attention split by query rows across threads; bits unchanged

@ashhart ashhart left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @gilby, small ones: in clef_adapter.py's factor, the in > out branch divides by singular values that can be 0 and writes NaN factors as bf16; please guard it, assert the factors are finite, and add a rank-deficient wide test case. head_rows raises a bare KeyError when the merge lacks the embedding; a clear message would help. (The unit tests skip without torch, which is fine, but say so in the tool's docs.)

@gilby

gilby commented Oct 26, 2026

Copy link
Copy Markdown
Contributor Author

Thanks. Rebased on #1364's new head, plus one commit (65c6f0f4b):

  • factor, in > out: the rank is cut to the singular values above s[0] * 1e-7, so nothing divides by a zero (or near-zero) singular value, and an all-zero delta is refused. After the bf16 cast, any non-finite value in A or B stops the tool with the module's name.
  • New test test_a_rank_deficient_wide_delta_keeps_its_rank_and_stays_finite: a 16×48 down_proj whose delta changes only 2 rows (exact in bf16). It wrote NaN factors before this fix; now it recovers rank 2 and the delta exactly.
  • head_rows: a merge without the output embedding now stops with "… is in the base but not in the merge: the head needs the output embedding". A test covers it.
  • Docs: the docstring says the tests need torch and safetensors and are skipped without them.

tools/tests/test_clef_adapter.py: 4/4 pass with torch.

@ashhart

ashhart commented Oct 26, 2026

Copy link
Copy Markdown
Owner

Review against main e9b69ed (shared notes on #1361). The tool keeps min(rank, out, in) eigenvectors and rounds both factors to BF16 (tools/clef_adapter.py:99–:117), so the extracted adapter is an approximation of the merged weights; the header's "factored exactly" isn't accurate, and the measured residual is never refused (:119, :204). Since we can't serve an approximate adapter (see #1364), we'll close this rather than carry it. Two things worth fixing if you keep it for your own use: the out < in branch divides by every selected singular value, including zero for rank-deficient deltas (:112, :116), and the output directory is written before validation finishes.

@ashhart ashhart closed this Oct 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants