Skip to main content

Nightly Precision Regression Testing

Overview

The nightly precision regression framework detects silent numerical regressions in the SGLang serving engine by comparing per-layer hidden states between consecutive runs. It runs as a nightly CI job on 8×H200 GPUs and can also be invoked locally for development and debugging. The framework operates on a rolling-baseline model:
  1. Baseline creation or comparison: Launch the server, send a fixed prompt, dump per-layer hidden states to disk. If a previous baseline exists, compare the new tensors against it using the SGLang tensor comparator. If the comparison passes, the new tensors become the updated baseline.
  2. On the first run (or when the capture shape changes), the dumped tensors are saved as a new baseline with no comparison.
Baselines are stored locally on disk and synced to a HuggingFace dataset so they survive across CI runners and can be shared across machines. The HF dataset store is required — the test errors if SGLANG_PRECISION_HF_REPO is unset.

How It Works

Step-by-step flow

Key components


What Gets Dumped and Compared

Strided layer capture

Not every layer is dumped — the framework uses a strided capture to reduce I/O and storage overhead. By default, it captures:
  • Layer 0 (always)
  • The last layer (always)
  • Every 8th layer in between (configurable via LAYER_CAPTURE_STRIDE)
The layer count is resolved automatically from the model’s HuggingFace config.json (num_hidden_layers or num_layers). If resolution fails, all layers are captured as a safe fallback. The dumper filter is built dynamically as a regex matching only the selected layer indices, e.g.:

Decode-path verification

The test generates 2 tokens with ignore_eos=True to ensure the model’s decode path is exercised. After the dump, _assert_decode_captured() verifies that tensors from the decode step were actually captured (not just prefill). If only prefill tensors are found, the test fails immediately — this catches misconfigurations where --max-total-tokens is too low for the decode loop to run.

Comparator

The comparator computes relative differences (rel_diff) for each tensor and checks them against a configurable threshold (default 1e-3). For tensor-parallel models, the --override-dims flag tells the comparator how to reduce across TP ranks before comparing:
This sums partial TP contributions along the hidden dimension before computing the diff, so the comparison is semantically correct even with TP > 1. If the comparator returns exit code 0 but compared zero layers (baseline/target name mismatch), the test fails with a diagnostic message rather than silently passing.

Capture signature

A capture_signature (SHA-1 hash of schema version, max_tokens, ignore_eos, TP size, and dumper filter) is computed per run. The HF store uses this signature during fetch to ensure only baselines with an identical capture shape are considered. If the signature changes (e.g. you add layers to the capture set or change TP), the framework establishes a fresh baseline instead of erroring on incompatible tensors.

Environment Variables


CI Integration

Workflow job

The nightly job nightly-test-precision-8-gpu-h200 is defined in .github/workflows/nightly-test-nvidia.yml and runs on an 8-GPU H200 runner. It is included in the nightly suite via test/run_suite.py. Key CI configuration:

Required GitHub secrets/variables

GitHub Step Summary

When running in CI, the test writes a Markdown table to the GitHub Actions job summary showing each model’s status (PASSED, FAILED, BASELINE_ESTABLISHED, or ERROR).

HF Dataset Storage Layout

Baselines are organized in the HF dataset as:
A top-level manifest.jsonl tracks all runs with one JSON object per line. Each manifest row carries a capture_signature field so that fetch selects only baselines with a matching capture shape. The prune_old_runs() function (callable manually) retains daily runs for 30 days and keeps one run per week beyond that window.

How to Add a New Model

Option A: Add to the default model list (CI)

Edit the default in test/registered/debug_utils/test_nightly_precision_regression.py:
Or set the SGLANG_PRECISION_MODELS environment variable in the CI workflow to override the default.

Option B: Run locally for a specific model

Step-by-step: adding a model to the nightly CI

  1. Verify the model works with the dumper. Run locally first to ensure hidden states are captured correctly:
  2. Run a comparison pass (remove FORCE_UPDATE):
    This should report PASSED if the engine is numerically stable for the model.
  3. Set the tensor-parallelism size. If the model requires TP > 1, the test harness defaults to tp_size=8 for all models. To customize, modify the ModelLaunchSettings construction in the test or pass extra server arguments:
  4. Adjust the diff threshold if needed. FP8 or quantized models may exhibit larger numerical differences. Set SGLANG_PRECISION_DIFF_THRESHOLD to an appropriate value (e.g., 1e-2 for FP8).
  5. Add to the default model list or configure SGLANG_PRECISION_MODELS in the CI workflow.

Considerations for model-specific adjustments


Running Locally

Prerequisites

  • SGLang installed in development mode
  • GPUs matching the model’s requirements
  • huggingface_hub installed
  • A HuggingFace dataset for baseline storage and a write-capable HF_TOKEN. The HF store is mandatorySGLANG_PRECISION_HF_REPO must be set or the test will error at startup. This is because the nightly CI runners are ephemeral (no persistent local disk), so baselines must survive across runs via the HF dataset. There is currently no local-only fallback.

Quick local test

Force-refresh a baseline


Interpreting Results

Status codes

Output example

When a failure is detected

  1. The comparator output is saved to /tmp/nightly_precision_<model>_*.log
  2. The failing tensors and comparator report are pushed to the HF dataset with pass_label="failed" for offline diagnosis
  3. The GitHub Step Summary includes the failure details
  4. The CI job exits with a non-zero status

Baseline Management

Local baselines

Baselines are stored at:
A baseline_meta.json next to the tensors records the timestamp and commit that produced the baseline.

HF dataset baselines

  • Fetch: At test start, if no local baseline exists, the latest signature-matched baseline is downloaded from the HF dataset.
  • Push: After each run, tensors and metadata are uploaded to the dataset.
  • Prune: Use prune_old_runs() to garbage-collect old baselines (keeps 30 days of daily runs, one per week after that).

Refreshing a stale baseline

If an intentional numerical change (e.g., kernel optimization, model refactor) causes a comparison failure:
  1. Verify the change is intentional
  2. Set SGLANG_PRECISION_FORCE_UPDATE=1 and run the test once to establish a new baseline
  3. Commit any necessary threshold adjustments
If you change the capture configuration (stride, TP size, etc.), the capture_signature will differ and the framework automatically establishes a fresh baseline — no manual intervention needed.

Known Limitations

Baseline drift

The framework uses a rolling baseline: every successful comparison updates the baseline to the current run’s tensors. This means the reference shifts forward each day. While individual day-to-day diffs stay within the configured threshold, tiny numerical differences can accumulate over time, causing the baseline to silently drift away from the original golden values. Implications:
  • The framework detects regressions (a sudden, large numerical change between consecutive runs), not absolute accuracy relative to a fixed reference.
  • Over weeks or months, the cumulative drift may become significant enough to mask a real regression that happened gradually, or to cause a false-positive failure when the drift eventually crosses the threshold.
Mitigation strategies (not yet implemented):
  • Periodically re-establish a fresh anchor baseline from a known-good reference commit.
  • Track the cumulative drift in the manifest metadata and alert when it exceeds a long-term budget.
  • Compare against a fixed “epoch” baseline in addition to the rolling one.

No local-only mode

The test requires a HuggingFace dataset (SGLANG_PRECISION_HF_REPO) and a write-capable HF_TOKEN. There is no local-only fallback. This is by design — CI runners have no persistent local disk, so the HF dataset is the only way to carry baselines across runs. If you need to run the test locally, you must set up a HF dataset (even a private one) and provide the corresponding token.

File Reference