Official-Continuation Quality Testing
This directory contains the prompts, tracked official fixtures, and scripts used to compare local GGUF variants against hosted-model continuations.
The main metric is target-token negative log likelihood: collect an official continuation, then ask each local GGUF how much probability it assigns to that exact continuation token by token. This avoids judging quality from one sampled answer.
1. Tracked Fixture Sets
Curated fixtures are kept in the repository so release QA can run without calling hosted APIs:
data/glm52-openrouter-100: 100 GLM 5.2 continuations collected through OpenRouterz-ai/glm-5.2withtop_logprobs=20.data/glm53-flash-openrouter-zai-fp8-100: 100 GLM 5.3 Flash continuations from OpenRouter's pinned Z.AI FP8 endpoint. The endpoint does not expose logprobs, so these are deterministic continuation fixtures.data/glm53-flash-openrouter-zai-fp8-long: eight code-context continuations from the same FP8 endpoint, with 3K-7K prompts that exercise sparse attention. Use the rendered prompts below to preserve the API's reasoning prefix.data/flash: 100 DeepSeek V4 Flash 0731 continuations collected from the official DeepSeek API withtop_logprobs=20.data/pro: 100 DeepSeek V4 PRO preview continuations collected from the official DeepSeek API withtop_logprobs=20.data/pro-0813: 100 DeepSeek V4 PRO 0813 continuations collected from the official DeepSeek API withtop_logprobs=20.data/qwen38-flash-alibaba-100: 100 non-thinking Qwen3.8 Flash continuations from Alibaba through OpenRouter, with top-five logprobs.data/qwen38-flash-alibaba-long: 12 archive/code continuations from the same endpoint, with prompts from 2K to 24K tokens.- deepseek-v4.1-flash-20260919-router: 112 fresh official V4.1 Flash continuations, including 12 longer prompts up to 23K tokens. Temperature-1 samples with top-20 logprobs, intended for paired numerical comparisons. These are not V4 Flash or Vision-Exp vectors.
DeepSeek V4 Flash also has tracked official smoke vectors in
tests/test-vectors/. Those vectors drive ./ds4_test --logprob-vectors and
include short prompts plus long-prompt attention cases.
The hosted APIs expose output-token logprobs and top-logprob alternatives, not full vocabulary logits.
The scorer verifies each API token's bytes against the local token boundaries, not just the number of tokens. If they differ, it still scores the continuation text but skips API logprob comparisons for that case. Alternatives containing Unicode replacement characters are excluded because the original token bytes may have been lost by the provider.
2. Collect Official Continuations
The Flash 0731 fixture is historical. The official API now redirects the old
Flash names to V4.1; do not overwrite data/flash with new calls. Collect
current V4.1 references in a separate directory:
export DEEPSEEK_API_KEY=...
python3 gguf-tools/quality-testing/collect_official.py \
--model deepseek-flash \
--endpoint https://api.deepseek.com/chat/completions \
--prompts gguf-tools/quality-testing/prompts.jsonl \
--out /tmp/deepseek-v4.1-flash-new \
--count 100 \
--max-tokens 24 \
--top-logprobs 20 \
--thinking disabled \
--reasoning-effort omit
For GLM 5.2 through OpenRouter:
export OPENROUTER_API_KEY=...
python3 gguf-tools/quality-testing/collect_official.py \
--model z-ai/glm-5.2 \
--endpoint https://openrouter.ai/api/v1/chat/completions \
--api-key-env OPENROUTER_API_KEY \
--prompts gguf-tools/quality-testing/prompts.jsonl \
--out gguf-tools/quality-testing/data/glm52-openrouter \
--count 100 \
--max-tokens 24 \
--top-logprobs 20 \
--token-limit-field max_tokens \
--provider-order parasail/fp8 \
--require-parameters \
--thinking omit \
--reasoning-effort none
For GLM 5.3 Flash through the pinned Z.AI FP8 endpoint:
export OPENROUTER_API_KEY=...
python3 gguf-tools/quality-testing/collect_official.py \
--model z-ai/glm-5.3-flash \
--endpoint https://openrouter.ai/api/v1/chat/completions \
--api-key-env OPENROUTER_API_KEY \
--prompts gguf-tools/quality-testing/prompts.jsonl \
--out gguf-tools/quality-testing/data/glm53-flash-openrouter-zai-fp8-100 \
--count 100 \
--max-tokens 128 \
--top-logprobs 0 \
--token-limit-field max_tokens \
--provider-order z-ai/fp8 \
--thinking omit \
--reasoning-effort low
Use one output directory per checkpoint. For PRO 0813 through the official DeepSeek API:
python3 gguf-tools/quality-testing/collect_official.py \
--model deepseek-v4-pro \
--prompts gguf-tools/quality-testing/prompts.jsonl \
--out gguf-tools/quality-testing/data/pro-0813 \
--count 100 \
--max-tokens 24 \
--top-logprobs 20
The script writes:
data/<model>/prompts/case_*.txtdata/<model>/continuations/case_*.txtdata/<model>/responses/case_*.jsondata/<model>/manifest.tsv
The prompt list is tracked in prompts.jsonl. Curated fixture directories are
also tracked after review; ad-hoc API collection directories should stay
untracked until they are intentionally promoted into the release QA set.
Use --system TEXT when the reference needs an explicit system message.
--system '' sends an empty message, which is different from omitting it:
some providers insert a default system prompt when none is supplied. The
collector records this setting and rejects a changed system message on resume.
For a nonempty system message, render the matching local chat prompt and use
--rendered-prompt; the scorer's ordinary prompt mode adds no system message.
For Qwen, use the following settings in a new output directory:
python3 gguf-tools/quality-testing/collect_official.py \
--out /tmp/qwen-official \
--model qwen/qwen3.8-flash \
--endpoint https://openrouter.ai/api/v1/chat/completions \
--provider-order alibaba --require-parameters \
--require-response-provider Alibaba --require-response-model qwen/qwen3.8-flash \
--system '' --thinking omit --reasoning-effort none \
--count 100 --max-tokens 64 --top-logprobs 5
This reads OPENROUTER_API_KEY from the environment and disables provider
fallbacks. Omitting --system '' caused Alibaba to add 13 prompt tokens in
the September 2026 checks. The empty message matches Qwen's published
no-thinking template and the scorer's ordinary prompt mode.
Alibaba does not disclose this endpoint's precision. The official model card describes hosted Flash as based on Flash Next, not as a byte-identical checkpoint. Agreement with this service is an external quality check, not proof of exact native-weight inference.
3. Build The Local Scorer
make -C gguf-tools quality-score
The scorer links against the DS4 runtime, using Metal on macOS and CUDA on
Linux. On a DGX Spark, pass CUDA_ARCH=sm_121 to the build command.
Build the optional llama.cpp control scorer with:
make -C gguf-tools quality-llama-score
4. Score GGUF Variants
gguf-tools/quality-testing/score_official \
../deepseek-v4-quants/gguf/OLD.gguf \
gguf-tools/quality-testing/data/pro/manifest.tsv \
/tmp/old.tsv \
4096
gguf-tools/quality-testing/score_official \
../deepseek-v4-quants/gguf/NEW.gguf \
gguf-tools/quality-testing/data/pro/manifest.tsv \
/tmp/new.tsv \
4096
Use data/flash/manifest.tsv for Flash GGUFs and
data/glm52-openrouter-100/manifest.tsv for GLM 5.2 GGUFs. Use
data/glm53-flash-openrouter-zai-fp8-100/manifest.tsv for GLM 5.3 Flash. Use
data/pro/manifest.tsv for the PRO preview checkpoint and
data/pro-0813/manifest.tsv for PRO 0813. The scorer and comparator do not
care which model produced the manifest; the manifest path selects the
continuation set.
Add --continued-prefill N to process each prompt in two calls, leaving its
last N tokens for the second call. This checks continued-prefill quality
against the same official answers. Every prompt must contain more than N
tokens; otherwise the scorer fails instead of silently skipping the test.
Add --session-batch N (2 to 8) to score an official continuation alongside
unrelated, independently advancing sessions. The scorer rotates their row
order on every step. Compare against a run without this option using the same
manifest, model and context; budget memory for all N sessions.
Check session isolation separately on a dedicated Metal host:
DS4_TEST_MODEL=MODEL.gguf DS4_TEST_BATCH_ISOLATION=1 \
DS4_TEST_SESSION_COUNT=4 DS4_TEST_DECODE_STEPS=32 \
MTL_DEBUG_LAYER=1 ./tests/test_metal_session_batch
This requires exact logits when companion prompts and row order change, then
checks a continued prefill and resumed serial decoding. It also checks that
invalid batches leave the target unchanged. It does not replace serial/batch
quality comparisons: different arithmetic can round differently without
sessions contaminating one another. Use DS4_TEST_PROMPT_FILE and
DS4_TEST_CONTEXT_SIZE to repeat at longer prefixes. For physical TP, the test
also accepts DS4_TEST_TP_RDMA_DEVICE and DS4_TEST_TP_GID_INDEX alongside
its coordinator/worker settings. Throughput measurements must run separately
without Metal API validation.
For Metal V4.1 prefill scheduling changes, also run:
make tests/test_deepseek41_prefill
./tests/test_deepseek41_prefill --dispatch
MTL_DEBUG_LAYER=1 ./tests/test_deepseek41_prefill MODEL.gguf speed-bench/promessi_sposi.txt
The model test uses SSD streaming and two 128K sessions. Run it alone on a dedicated host with at least 128 GiB RAM. It alternates small and large appends through 113K context, checking dispatch, progress, unchanged-prefix reuse, saved state and subsequent decoding against a control without decoder deferral. Keep the official continuation checks too: this scheduling test does not judge quality across different floating-point operation orders. Measure speed without Metal API validation, separately for initial and continued prefills.
Run the same mixed-prefix test over physical RDMA on two dedicated Metal hosts. Start the worker with the same model and a 131072-token context, then the test on the coordinator (replace the host and RDMA device names):
# Worker
MTL_DEBUG_LAYER=1 ./ds4 -m MODEL.gguf --ctx 131072 \
--tensor-parallel --role worker --coordinator COORDINATOR 19455 \
--transport rdma --rdma-device WORKER_DEVICE --rdma-gid-index 1
# Coordinator
MTL_DEBUG_LAYER=1 ./tests/test_deepseek41_prefill --tensor-parallel \
MODEL.gguf speed-bench/promessi_sposi.txt COORDINATOR 19455 COORDINATOR_DEVICE 1
This compares equal TP prefill partitions and queued versus synchronous decode, including full logits and cache state. TP snapshots rebuild both ranks from tokens, so restoration is compared with an equivalent fresh replay. Also run the official scorer in TP mode for initial and continued prompts; scheduling parity alone does not establish model quality.
Add --quality to disable DS4's speed-oriented numerical shortcuts. For an
independent llama.cpp comparison of a DeepSeek V4 GGUF, use the same manifest
and the token-identical DS4 prompt renderer:
gguf-tools/quality-testing/score_llama \
/path/to/model.gguf \
gguf-tools/quality-testing/data/flash/manifest.tsv \
/tmp/llama.tsv \
4096 \
deepseek-ds4
GLM's hosted endpoint requires reasoning. A no-thinking local prompt does not
match a reply generated after reasoning, even at temperature zero. For the long
GLM fixtures, render the official Reasoning Effort: Low template and include
the returned reasoning before scoring the answer:
python3 gguf-tools/quality-testing/render_glm_references.py \
gguf-tools/quality-testing/data/glm53-flash-openrouter-zai-fp8-long
gguf-tools/quality-testing/score_official /path/to/GLM-5.3-Flash-Q2.gguf \
gguf-tools/quality-testing/data/glm53-flash-openrouter-zai-fp8-long/manifest-rendered.tsv \
/tmp/glm-long.tsv 8192 --rendered-prompt
--rendered-prompt reads the prompt file with its existing chat markers; it
does not add another chat template. The renderer can also be applied to the
older 100-case GLM 5.3 Flash fixture. Keep rendered and legacy no-thinking
scores separate: their prefixes differ. Z.AI supplies no token logprobs, so
judge these runs by continuation NLL and greedy agreement, not logprob parity.
For a full-residency vs SSD-streaming comparison, score the same model twice and add the streaming flags to one run:
gguf-tools/quality-testing/score_official \
/path/to/model.gguf \
gguf-tools/quality-testing/data/glm52-openrouter-100/manifest.tsv \
/tmp/streaming.tsv \
4096 \
--ssd-streaming
5. Compare
python3 gguf-tools/quality-testing/compare_scores.py /tmp/old.tsv /tmp/new.tsv
Output fields:
avg_nll: average negative log likelihood; lower is better.delta_new_minus_old: negative means the new GGUF fits the official continuation better.case_wins_new_old_ties: per-prompt NLL wins.first_token_matches: how often the local greedy first token matches the official first token.avg_greedy_lcp: average greedy longest common prefix against the official continuation. This measures exact agreement, not general answer quality; one early mismatch discards all later agreement. Do not use it alone as a quality gate, especially for sampled references. Compare paired NLL and API probability agreement under matching execution settings as well.api_target_mae: when the manifest includesresponse_file, absolute local-vs-API logprob delta for aligned official output tokens.api_top_coverage: fraction of API top-logprob alternatives that map exactly to one local tokenizer token.api_top1_rate: how often the API top alternative equals the local greedy token.api_topn_recall: fraction of mapped API top-N alternatives found in the local top-N for the same position.api_top_mae: local-vs-API logprob MAE over mapped API top alternatives.api_pair_rate: pairwise ordering agreement among mapped API alternatives.
6. Validate regression artifacts
Two standard-library Python tools check saved artifacts without running inference. For matched score_official runs with API logprob coverage, require the complete intended manifest and finite, consistent counts and scores:
python3 gguf-tools/quality-testing/validate_scores.py \
--manifest gguf-tools/quality-testing/data/flash/manifest.tsv \
--baseline /tmp/old.tsv --candidate /tmp/new.tsv --strict-identical > /tmp/scores.json
Exit 0 with --strict-identical requires every per-case value to match; omit it to report valid differences without deciding their acceptability. Unlike compare_scores.py, validation rejects missing/duplicate cases, malformed fields and changed coverage denominators. This stricter mode requires the current four-column manifest and scorer TSV format with nonzero API coverage; use the existing comparator for continuation-only references without API logprobs.
Compare complete vocabulary dumps from ds4-bench --dump-frontier-logits-dir:
python3 gguf-tools/quality-testing/compare_frontier_logits.py \
/tmp/old-logits /tmp/new-logits --frontiers 2048 4096 --ctx 8192 \
--model /models/model.gguf --backend rocm --quality false --quant-bits 2 --vocab 129280 \
--output /tmp/frontiers.json
Supply the exact expected metadata: ctx is allocated capacity; each frontier is total context, and prefill is its increase from the previous frontier (initially zero). Each directory must contain exactly the requested dumps. Exit 0 requires complete finite float32 bit identity; exit 1 reports drift, invalid artifacts or an operational error. JSON distinguishes identical, drift and invalid, with full-vector hashes, differing counts and maximum absolute differences. The output path must be new. The parser follows the writer's nine-significant-digit float format and preserves signed zero.
Matching argmax is insufficient: a measured 8K–64K comparison retained all four argmax IDs while full vectors differed, with maximum absolute difference 7.45228. This is numerical drift, not by itself proof of worse generation quality. Record prompt/weight hashes, source/build identity, settings and successful run completion separately; these tools cannot recover that provenance from score tables or dumps, and do not replace model-quality checks.
Run the host-only positive and negative controls:
python3 -m unittest discover -s gguf-tools/quality-testing/tests -v
7. Qwen3.8 Spark Baseline
Measured on 2026-09-15, using one DGX Spark per quant. The short set has 100 cases and 5,696 target tokens; the long set has 12 cases and 766 target tokens, with prefixes from 1,971 to 23,985 tokens. These are ordinary decoding and prefill checks, without MTP or vision.
| Suite | GGUF | API top-token agreement | NLL, default | NLL, --quality |
|---|---|---|---|---|
| Short | Q2 | 88.90% | 0.352980 | 0.352915 |
| Short | Q4 | 92.08% | 0.290297 | 0.290538 |
| Long | Q2 | 93.99% | 0.154199 | 0.154106 |
| Long | Q4 | 97.26% | 0.124611 | 0.125546 |
Agreement is measured after feeding the same official continuation prefix, not as a percentage of correct answers. The two short cases with damaged emoji metadata contribute to NLL but not API agreement, which covers 5,568 short-set tokens. All 766 long-set tokens align. Every local prompt and target token ID was also checked against Qwen's published tokenizer.
The default fast path shows no material loss relative to --quality in these
sets. The paired case-bootstrap intervals include zero for both quants and
both suites. Q4 is closer to the hosted reference than Q2 on average.
These measurements do not prove identical native weights or correctness at
every context length.
Leaving the last 1 or 256 prompt tokens for a separate continued prefill changed aggregate NLL by less than 0.0008 for either quant. Independent CLI answers at about 2K and 24K context also retrieved the correct archive owner and revision, and gave the correct clamp outputs and boundary tests, for both Q2 and Q4.
Raw per-case scores, weight/source hashes and comparison details are retained in short results and long results.