Files
TheTomandClaude Opus 4.7 a4c59a0b05 REFRACT v0.3.2: alpha release — 4 axes, 4 subcommands, 3 backends, 9 docs
Compressed history of v0.1.4 → v0.3.2 in one alpha-ship commit so the
framework is testable end-to-end by friends. Detailed per-version
discussion lives in refract/CHANGELOG.md.

What ships
----------

Four scoring axes vs the model's own fp16-KV reference:
  - Axis A (Trajectory, v0.1.4) — decode-time token IDs via patched
    llama-completion, no detokenize round-trip. Replaces the buggy
    v0.1.x GTM as the default.
  - Axis B (KLD@D) — corpus KL divergence, bit-exact zero on Metal
    when ref==cand. Native MLX implementation in addition to the
    llama-perplexity subprocess path.
  - Axis C (R-NIAH, v0.2.0) — long-context retrieval at multiple
    (length, position) cells. Confidence guard for low base_acc.
  - Axis D (PLAD, v0.2.0) — perturbation-locality drift across
    typo/case/punct/paraphrase. Skipped-perturbation handling.

Composite = harmonic mean. Bands EXCELLENT/PASS/DEGRADED/FAIL.

Four CLI subcommands:
  - score          — run REFRACT on a candidate KV config
  - selftest       — 30s preflight: binaries, flags, model probe
  - compare        — multi-report side-by-side
  - repeatability  — run N times, report stdev/range health verdict

Three backends:
  - llamacpp (production)        subprocess wrappers around llama.cpp
  - mlx (production)             native via mlx-lm; verified end-to-end
                                 on Qwen2.5-3B-Instruct-4bit
  - vllm (skeleton)              interface defined; plug-in pointers
                                 in module docstring

Reporting
---------

  - Text (ANSI colour, terminal)
  - JSON schema refract.report.v0.3.1 with framework_version +
    environment metadata (backend, llama_cpp_commit, mlx_lm_version)
  - HTML (self-contained single file) with hardware info, model
    params, repro command, R-NIAH heatmap, PLAD per-perturbation bars

Discoveries from the matrix runs (all logged in CHANGELOG):
  - "secret password" needle wording triggers safety refusals on
    instruct models. v0.2.1 switched to neutral "rare paint color"
  - Thinking-mode models burn n_predict before answering. v0.3.0 added
    -rea off + auto-detect at startup
  - Symmetric turbo4/turbo4 is the canonical FAIL config; framework
    correctly identifies (composite ~19) on gemma-4-26B-A4B-Q8

Documentation
-------------

  - README.md         what + alpha banner + "where do I go" navigator
  - QUICKSTART.md     0 to first PASS in 30 minutes
  - INTERPRETATION.md per-axis "what to do if low" guide
  - LEADERBOARD.md    cross-model rankings + "ranks the quant, not the
                      model" disclaimer
  - PITFALLS.md       known traps from today's matrix runs
  - LIMITATIONS.md    what v0.3 explicitly does not do
  - CHANGELOG.md      version history with discovery stories
  - MATRIX-RESULTS.md the 7-model matrix log
  - examples/        4 sample JSONs + HTML reports + README

Tests
-----

80 → 82 unit tests, all green:
  - 17 trajectory tests (incl. R-NIAH/PLAD impls + 4-axis composite)
  - 5 strip-noise pinning tests
  - 14 unit tests (math + KVConfig)
  - 8 JSON schema tests (incl. v0.3.1 framework_version field)
  - 6 KLD regex tests
  - 5 corpus-identity tests
  - 5 tokenize-to-ids tests
  - 4 command-construction tests

Verified
--------

  - 7-model v0.3.0 matrix end-to-end (~2h on M5 Max 128GB)
  - Llama-4 Scout extras + symmetric-turbo negative control
  - MLX backend on Qwen2.5-3B-Instruct-4bit
  - selftest on llamacpp (no model) and mlx (with model)
  - 2-run repeatability on qwen2.5-1.5b: composite stdev = 0.00 on
    Trajectory + KLD (bit-exact reproducibility on cheap axes)

Open follow-ups (v0.4)
----------------------

  - vLLM backend implementation (skeleton today)
  - T-Call axis (tool-call fidelity) building on v0.3 chat-template
  - Bundled corpus + prompts distribution
  - `refract leaderboard --in dir/ --out LEADERBOARD.md` auto-gen

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 15:13:04 -05:00

7.3 KiB
Raw Permalink Blame History

REFRACT INTERPRETATION GUIDE

How to read a REFRACT report card. Targets a reader who knows quants generally but is new to REFRACT specifically.

Composite score

A single 0–100 number. Higher is better. Computed as the harmonic mean of every axis that ran (so any single broken axis tanks the composite — that's intentional, the framework is fail-loud).

Band Range Meaning
EXCELLENT 90–100 Within reference noise. Indistinguishable.
PASS 80–90 Minor drift; safe to deploy in most uses.
DEGRADED 60–80 Visible drift; audit on your workload first.
FAIL 0–60 Material quality loss; treat as broken.

Per-axis interpretation

Each axis has its own 0–100 score and its own band. The Diagnosis block in the report card translates the per-axis pattern into plain English. Below is the deeper "what to do about it" guide per axis.

Axis A: Trajectory (formerly GTM)

Measures: Token-level agreement with fp16. Greedy-decodes N tokens under both reference and candidate KV configs, captures token IDs at decode time, computes prefix agreement length / candidate length.

  • EXCELLENT (90–100): the model decodes essentially the same tokens under both configs. KV quantization is doing nothing visible in the short-context greedy regime.
  • PASS (80–90): tokens diverge after 5–10 steps but the leading context matches. Common for fine-grained quants. Safe.
  • DEGRADED (60–80): divergence within first few tokens on most prompts. Suspect a softmax-shift class of error: e.g. K rotation collisions, V rotation breaking with insufficient bits.
  • FAIL (0–60): model decodes essentially different text under cand. The KV scheme is wrecking next-token distributions.

Remediation when low: try a higher-bit V cache; switch from symmetric to asymmetric turbo; try non-rotating V. If cand and ref diverge at step 1 (median_first_divergence near 0), the problem is purely at the per-token distribution level.

Axis B: KLD@D (corpus KL divergence)

Measures: Distribution-level divergence from fp16 on natural-text inputs, averaged per token across a corpus. Bit-exact zero on Metal when cand == ref, so any non-zero value is real signal not float jitter.

  • EXCELLENT (90–100, KLD < 0.05 nats): distributions essentially match. The candidate doesn't measurably move the model.
  • PASS (80–90, KLD ~0.05–0.2 nats): small but measurable drift. Most quants land here.
  • DEGRADED (60–80, KLD ~0.2–0.5 nats): material distribution shift. Argmax may still match (so trajectory could be high) but the second/third choices have moved.
  • FAIL (< 60, KLD > 0.5 nats): large divergence; the per-token predictive distributions are fundamentally different.

Remediation when low: measure the noise floor first (--measure-floor) to confirm the reference itself is deterministic on your build. If floor passes, the candidate is genuinely lossy at the distribution level. Try a higher-bit baseline (q8 instead of q4) or a non-rotating V scheme.

Note: KLD@D is the best-disciplined axis. If KLD says broken, trust it. If KLD says clean while Trajectory says broken, the bug is likely in chat-template handling or in the test workload not the quant itself.

Axis C: R-NIAH (Retrieval Needle-In-A-Haystack)

Measures: Long-context retrieval quality vs fp16. Inserts a sentinel fact into a long context at fractional positions; checks whether the candidate retrieves it as well as the reference does. Score per (length, position) cell, aggregated.

  • EXCELLENT (100): candidate retrieves wherever the reference does. Either the model is robust at long context OR the model can't retrieve at all under either config (check confidence field).
  • PASS / DEGRADED / FAIL (< 100): candidate loses retrieval at cells where the reference still finds the needle. Inspect the per-cell breakdown for the (length, position) where it breaks.

Remediation when low: revert to a higher-bit KV at long contexts; or add a runtime guard that switches KV schemes by sequence length.

Confidence: the JSON includes confidence: low when base_acc_avg < 0.2 — the model isn't engaging retrieval at all and R-NIAH = 100 is then a noise-floor reading. Re-check on a stronger model or with a different needle.

Axis D: PLAD (Perturbation-Locality Aware Drift)

Measures: Robustness to small prompt changes vs fp16. Generates anchor completions, perturbs each prompt minimally (typo, casing, punctuation, paraphrase), measures how much the candidate's output drifts vs how much the reference's drifts.

Per-perturbation breakdown is the actionable signal:

  • typo low → candidate is brittle to typos. Consider input sanitization or higher-bit fallback when typos are detected.
  • case low → candidate is sensitive to capitalization changes.
  • punct low → candidate's output flips on terminal punctuation (sometimes acts as an EOS-like signal).
  • paraphrase low → candidate is sensitive to lexical substitution. Less common; usually the most worrying signal.

Confidence: if a perturbation can't apply to your prompts (e.g., no ≥4-char words → typo skipped, no synonyms in table → paraphrase skipped), it's listed in skipped_perturbations and excluded from the band.

Pattern recognition (Diagnosis block)

The Diagnosis block fires plain-English sentences for the most common patterns. Some examples and what they imply:

  • "Per-token distribution is broken but high-level surfaces are intact" → Trajectory and KLD low, R-NIAH and PLAD intact. The quant is producing different tokens at decode time but reasoning and retrieval still work. Common for aggressive turbo schemes. Likely safe if you don't depend on exact text reproduction.

  • "Long-context retrieval degraded" → R-NIAH low, others high. Candidate fails specifically at long context. Affects RAG, summarization, code understanding. Unsafe for those workloads.

  • "Brittleness to small input changes" → PLAD low, others high. Candidate flips its output under input variations. Affects real-user deployments where prompts have typos or slight paraphrasing. Stress-test before shipping.

  • "Catastrophic" → every measured axis < 60. Treat as non-functional; revert to higher-bit config.

Composite vs per-axis: which to trust

If composite says PASS but one axis is DEGRADED, read the per-axis band first. Composite is a summary; per-axis tells you what kind of failure you have. A 95 composite hides a 99/99/99/85 reality where the model is actually brittle to perturbations.

For shipping decisions:

  • All 4 axes ≥ 80 → safe
  • One axis 60–80 → audit the specific surface that's degraded; decide based on whether that surface matters to your workload
  • One axis < 60 → ship only if you understand the failure and have mitigations
  • Multiple axes < 60 → don't ship

Cross-version comparisons

Reports embed framework_version. Comparing scores across versions is fine for the same model+candidate, but a "+1.5 composite" between v0.2.0 and v0.3.0 may be partly attributable to methodology changes (e.g., chat-template handling). Apples-to-apples requires same version on both sides.