How to Test GPT-OSS / Fable-5-Distilled GGUF Models Correctly
This document describes how to run HumanEval evaluation on GGUF-quantized GPT-OSS models (including Fable-5-Distilled) using llama-cpp-python. The key insight is that GPT-OSS uses a chat template with channels (analysis → final), which must be applied correctly for proper model behavior.
Test Results (HumanEval PASS@1)
Model
Quant
Score
Pass/Fail
Fable-5-Distilled
Q5_0 (67 GB)
99.39%
163/164
Fable-5-Distilled
Q8_0 (115 GB)
98.78%
162/164
gpt-oss-120b (base)
MXFP4
83.54%
137/164
The Q5_0 achieves a slightly higher score than Q8_0 on this benchmark (different quantization noise affects different problems). Fable-5-Distilled adds ~15-16% over the base model.
Without this template, the model produces garbage (e.g., # TODO comments or natural language descriptions instead of code).
2. No Stop Tokens
Do not set stop=["<|end|>"] — the model emits <|end|> after the analysis channel. If you stop there, you miss the final channel with the actual code. Use stop=[] and rely on max_tokens or the model's native EOS token.
3. Code Extraction
First look for ```python fenced blocks (the model often wraps code this way)
If no fence, extract from <|channel|>final<|message|>
Prefer code blocks that contain def or import (function definitions), not example usage blocks
Always take the first code block matching these criteria, not the last (the model may include example usage in later blocks)
4. Evaluation Without Prompt
The extracted code typically includes the full function definition (the model regenerates the signature). Evaluate using only the extracted code + test:
Do NOT prepend the prompt — doing so creates duplicate function definitions that cause exec errors.
5. Token Budget
Set max_tokens=8192. The model outputs analysis (reasoning) in the analysis channel before writing code. Complex problems may need 2000+ tokens for analysis alone, and another 500-2000 for the code.
Environment Notes
NCCL Compatibility
If you encounter undefined symbol: ncclCommWindowDeregister when importing transformers:
GGUF is the universal format for LLM inference. These files run on llama.cpp, LM Studio, Ollama, GPT4All, text-generation-webui, llamafile, MLX-LM, and any GGUF-compatible runtime — across macOS, Windows, Linux, iOS, and Android, on CPU, CUDA, Metal, Vulkan, and ROCm backends.
Model Overview
This is a distilled variant of the OpenAI gpt-oss-120b model, fine-tuned using MLX with LoRA adapters (rank=16, targeting all attention projections, MoE router, and expert FFN layers). The training was performed in the MLX ecosystem using the MXFP4 quantized base model format. These GGUF files are the first community-produced GGUF conversions of a LoRA-fine-tuned GPT-OSS model from the MXFP4 format.
Available Quantizations
Format
File
Size
Quality
Q8_0
gpt-oss-120b-Fable-5-Distilled-Q8_0.gguf
115.7 GB
Near-lossless (8-bit uniform, biases/norms kept at F32)
Q5_0
gpt-oss-120b-Fable-5-Distilled-Q5_0.gguf
~67 GB
Strong compression (5-bit symmetric, via llama-quantize from Q8_0)
Weight matrices use Q8_0/Q5_0; biases, layer norms, and attention sinks are stored at full F32 precision for numerical stability.
Conversion Pipeline
The conversion from MLX MXFP4 format to llama.cpp GGUF involved a two-phase pipeline addressing several novel technical challenges:
The source model is stored in Apple's MLX format using 4-bit MXFP4 quantization (Microscaling FP4, OCP spec) for the expert layers, with 8-bit affine quantization for attention projections. The LoRA adapter weights (576 tensors, float32) modify 288 modules spanning all Q/K/V/O projections, the MoE router, and all expert gate/up/down projections.
Key technical breakthroughs in Phase 1:
Per-weight quantization dispatch: The model uses heterogeneous quantization parameters — MXFP4 4-bit (experts), affine 8-bit (attention + embeddings), and a special group_size=64 variant for the router. Each weight's dequantization must use the correct (bits, group_size) pair from the config.
MXFP4 dequantization without explicit mode: MLX's dequantize function, when called with bits=4 and group_size=32, auto-detects the microscaling format. Explicit mode='mxfp4' is incompatible with the BF16 scale storage convention actually used.
LoRA fusion with shape-dependent matmul: The MLX LoRA adapter stores lora_a as [input_dims, rank] and lora_b as [rank, output_dims] for 2D weights, but [num_experts, rank, input_dims] and [num_experts, output_dims, rank] for 3D expert weights. The fusion requires lora_b.T @ lora_a.T (2D) vs lora_b @ lora_a (3D) — using the wrong formula produces catastrophic dimension mismatches.
BF16 overflow during MXFP4 dequant: The MXFP4 e8m0 scale representation can produce float32 values exceeding the BF16 representable range (~3.4e38), resulting in NaN values in ~5% of dequantized expert weights. These must be sanitized before downstream quantization.
Phase 2: BF16 → Quantized GGUF
Phase 2 reads the intermediate BF16 safetensors and writes the final GGUF in a single streaming pass, keeping RAM below 2 GB for a 218 GB intermediate.
Key technical breakthroughs in Phase 2:
Missing attention.key_length metadata: The llama.cpp GPT-OSS handler computes n_embd_head_k = n_embd / n_head = 2880 / 64 = 45 by default, which is incorrect (the true head dimension is 64). This causes a fatal GGML_ASSERT(a->ne[0] == b->ne[0]) crash during KV cache initialization. The fix is explicitly setting gpt-oss.attention.key_length, value_length, key_length_swa, and value_length_swa to head_dim=64 in the GGUF metadata.
Tokenizer vocabulary padding: The model's embedding table has 201,088 rows (vocab_size in config) but the o200k harmony tokenizer has only 200,019 entries. llama.cpp validates token_embd.weight against the tokenizer count. The token list must be padded with dummy tokens to reach 201,088.
Attention sinks must be F32: The ggml_soft_max_add_sinks and ggml_flash_attn_ext_add_sinks operations require the sinks tensor to be GGML_TYPE_F32. Storing it as BF16 causes a Metal backend crash during inference.
All biases must be F32: ggml_add operations in the attention and FFN graph require type-matched operands. Since attention outputs are F32, bias tensors stored as BF16 trigger binary_op: unsupported types errors on CPU and Metal.
GGUF tensor naming alignment: The GPT-OSS architecture in llama.cpp (PR #15091, LLM_ARCH_OPENAI_MOE) maps MLX tensor names via specific conventions: self_attn.o_proj → attn_output (not attn_out), mlp.router → ffn_gate_inp, mlp.experts.gate_proj → ffn_gate_exps, self_attn.sinks → attn_sinks.weight, etc. The architecture identifier is gpt-oss (hyphenated), not gptoss.
Safetensors byte-level data extraction: Reading individual tensors from safetensor files requires accounting for the JSON header size (f.seek(8 + header_length + offset), not f.seek(8 + offset)). Missing this causes the first tensor in each shard to read partially corrupt data — a silent data integrity bug.
The model uses GPT-OSS's native reasoning format. Set reasoning_effort via chat template kwargs for API usage. The chat template supports system, developer, user, and assistant roles with channel markers for analysis, commentary, and final output.
Local Agent Best Practice
This model demonstrates extremely strong reasoning capabilities — when given --jinja and --reasoning-format auto with reasoning_effort: "high", it can perform multi-step planning, complex code analysis, and structured problem decomposition at a level comparable to frontier models. However, it has a critical weakness: severe hallucination. The model will confidently fabricate facts, API signatures, file paths, URLs, and library versions.
Golden Rule: Always pair this model with the anysearch skill in llama-agent or opencode, which grounds responses against real web search results. Do not trust any factual claim from this model without verification.