Files
turboquant_plus/refract/PITFALLS.md
T
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

5.4 KiB
Raw Blame History

REFRACT — known pitfalls

Things that have actually bitten us, in priority order. Keep this discoverable so first-time users don't repeat the discovery.

Setup

Wrong default --axis-a in v0.1.x / v0.2.x

The gtm axis used llama-tokenize to retokenize the candidate's decoded text and compare it against the reference's retokenized text. Detokenize→retokenize is lossy: a 50-token greedy generation can come back as 60–137 tokens after retokenization (gemma-4 31B v0.1.2 hit a 2.87× inflation). v0.1.4 added the trajectory axis (decode-time token IDs via patched llama-completion); v0.3.1 made trajectory the default. If you're seeing weird Axis A scores, confirm --axis-a trajectory is in your invocation.

head_dim % block_size != 0 blocks q4/q8 KV

phi-2 and a few other models have head_dim = 80 which doesn't divide the q4_0 / q8_0 cache block size of 32. llama.cpp will fail to create the context with K cache type q4_0 with block size 32 does not divide n_embd_head_k=80. Use a different model OR pick a KV type compatible with the head dim (e.g., q4_K with block 256 if your build has it).

llama.cpp lacking turbo support

If you're testing TurboQuant configs (ctv=turbo4), your llama.cpp build needs the turbo branches compiled in. Check with:

llama-cli --help | grep -A2 'cache-type-k'
# look for 'turbo2, turbo3, turbo4' in the allowed values

If absent, build from TheTom/llama.cpp feature/turboquant-kv-cache or set --candidate "ctk=q8_0,ctv=q8_0" (standard quant) instead.

MLX RotatingKVCache + quantization

Gemma family + a few other sliding-window-attention models use RotatingKVCache in mlx-lm. As of mlx-lm 0.31.2, RotatingKVCache quantization is NotImplementedYet. Symptom:

NotImplementedError: RotatingKVCache Quantization NYI

If you hit this on MLX, either:

  • Switch to --backend llamacpp for that model, OR
  • Test with ctk=f16,ctv=f16 only on the MLX side

Result interpretation traps

R-NIAH = 100 with confidence: low

Means base_acc averaged across cells is below 0.2 — the model isn't engaging retrieval at all under either config. R-NIAH = 100 is then a noise-floor reading rather than real signal. Look at the confidence field in the JSON before posting the score.

Common causes (today, all v0.3+ should be fixed):

  • Older versions used "secret password" needle wording → triggered safety refusals on instruct models. v0.2.1 changed to neutral "rare paint color" framing.
  • Raw "Q: A:" prompt format didn't engage instruct models → they continued the wikitext stylistically. v0.3.0 applies chat templates via --jinja.
  • n_predict = 32 with thinking-mode models → answer truncated inside the thinking trace. v0.2.1 bumped default to 256.

PLAD paraphrase = NaN

The built-in synonym table is small. If your prompts have no words in the table, paraphrase never fires and the per-perturbation slot is NaN. The cell is listed in skipped_perturbations (v0.3.1+) and the overall PLAD score still uses the perturbations that DID fire. Don't read NaN as FAIL — read it as "didn't apply".

Trajectory + KLD low, R-NIAH + PLAD high

Means per-token distribution drift but high-level reasoning intact. Common for aggressive turbo configs on certain models. The candidate generates different text but still retrieves facts and resists perturbations. Useful triage signal. If your workload doesn't require text-level reproducibility (just task success), this may be safe. Otherwise revert to a less aggressive quant.

KLD low but Trajectory high

Means distributions look fine on the corpus but the model decodes different text. Two common explanations:

  1. The corpus distribution doesn't match the prompts the trajectory axis is using. Try a corpus that better reflects your workload.
  2. There's a chat-template engagement difference between the two code paths (KLD is corpus-driven and never touches chat templates; Trajectory does).

Composite shifts across versions

REFRACT v0.2.0 → v0.3.0 chat-template fix produced ±2 composite deltas on the same models / candidates. The number isn't a bug — the new methodology is correct, but cross-version comparisons require same version on both sides. Always include framework_version from the JSON when sharing scores.

Discovered during the v0.2/v0.3 matrix runs (2026-04-30)

  • "secret password" needle = safety refusal on RLHF'd instruct models. Fixed in v0.2.1 with neutral "rare paint color" framing.
  • Thinking-mode models burn n_predict before answering. v0.2.1 bumped R-NIAH n_predict to 256. v0.3.0 added auto-detect at startup + -rea off on llama-cli.
  • Symmetric turbo (ctk=turbo4,ctv=turbo4) is empirically worse than asymmetric on every distribution-level surface. This is the framework's negative-control config — if REFRACT doesn't FAIL it, the framework has problems. v0.2.0 result on gemma-4-26B-A4B: composite 19 FAIL, KLD 11.84 (2.13 nats), Trajectory 9.74. Use this as a sanity check.
  • R-NIAH substring matching is coarse on broken-distribution models — survives drift if the keyword shows up anywhere in the response (including thinking traces echoing the prompt).
  • --measure-floor is highly recommended for first runs — it verifies the reference itself is deterministic on your build. Without it, KLD deltas are theoretical.