Skip to content

Expand L4/L5 test coverage: seq2seq generation, golden data, ci_skip_reason - #186

Closed
justinchuby wants to merge 2 commits into
mainfrom
justinchu/expand-l4-l5-coverage
Closed

Expand L4/L5 test coverage: seq2seq generation, golden data, ci_skip_reason#186
justinchuby wants to merge 2 commits into
mainfrom
justinchu/expand-l4-l5-coverage

Conversation

@justinchuby

Copy link
Copy Markdown
Member

Summary

Expands L4/L5 test coverage with golden data for ~30 new models and adds seq2seq L5 generation support.

Changes

Infrastructure:

  • Add OnnxSeq2SeqGenerator in src/mobius/_testing/generation.py for encoder-decoder L5 generation tests
  • Add seq2seq to _GENERATION_SUPPORTED_TASKS in e2e_golden_test.py
  • Add ci_skip_reason YAML field + schema + dashboard support — separates "too large for CI" from "fundamentally broken"
  • CI detection via GITHUB_ACTIONS env var skips ci_skip_reason cases in CI only

Golden data (new):

  • Causal-LM: apertus-8b, arcee-afm-4.5b, ernie4_5-21b-moe, flex-olmo-2x7b, glm-4-9b, granite-moe-shared-7b, olmo3-7b, qwen3-30b-a3b
  • Seq2seq: bigbird-pegasus-large, led-base, mbart-large, pegasus-xsum, prophetnet-large, plbart-base, fsmt-wmt19
  • Encoder: deberta-v3-base, flaubert-base, rembert-base, xlm-roberta-xl, clip-text-model, xmod-base, roformer-chinese-small
  • Vision: cvt-13, mobilevit-small, mobilevitv2-1.0, pvt-v2-b0, segformer-b0, swin2sr, depth-anything-small
  • Vision-Language: gemma-3-4b-it, mllama

Skip reason triage:

  • Models with no safetensors weights → skip_reason
  • Models with code bugs (unknown activation, config issues) → skip_reason
  • Models too large for CI but locally runnable → ci_skip_reason

Fixes from PR #183 (cherry-picked):

  • 4D vision output handling in generate_golden.py
  • CLIP text model extraction
  • xmod language setting, plbart decoder_start_token_id fallback
  • Robust getattr for generation_config

justinchuby and others added 2 commits April 21, 2026 18:42
Introduce ci_skip_reason YAML field: models are skipped in CI
(GITHUB_ACTIONS=true) but run locally. Unlike skip_reason, it does NOT
block golden data generation.

Migrate 'too large for CI' and 'gated repo' skip_reasons:
- Remove skip entirely for <=4B models (now generateable)
- Migrate >7B models to ci_skip_reason

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Justin Chu <justinchu@microsoft.com>
- Add OnnxSeq2SeqGenerator for encoder-decoder L5 generation tests
- Add seq2seq to _GENERATION_SUPPORTED_TASKS in e2e_golden_test.py
- Generate golden data for ~30 new models across all task types
- Add ci_skip_reason YAML field for CI-only skip (too large for CI)
- Add skip_reason for models with no safetensors or code bugs
- Apply generate_golden.py fixes from PR #183 (4D vision, CLIP, xmod, plbart)
- Set min_token_match_ratio=0.1 for zamba2 (known Mamba divergence)
- Fix lint warnings in generation.py

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Justin Chu <justinchu@microsoft.com>
@github-actions

Copy link
Copy Markdown

🏗️ Architecture Diff

Comparing b75eb1d8f8d6de

Model Sub-model Changes Status

No architecture changes detected.


Legend: ⚪ No change · 🔵 Minor (attrs/inits) · 🟡 Moderate (nodes added/removed) · 🔴 Major (interface changed)

@github-actions

Copy link
Copy Markdown

Performance Comparison

Comparing b75eb1d8f8d6de

Model Metric Baseline Current Delta
bert (feature-extraction) model_size_bytes 359 KB 359 KB +0.0%
bert (feature-extraction) num_nodes 60 60 +0.0%
falcon model_size_bytes 364 KB 364 KB +0.0%
falcon num_nodes 66 66 +0.0%
gemma2 model_size_bytes 428 KB 428 KB +0.0%
gemma2 num_nodes 107 107 +0.0%
gpt2 model_size_bytes 388 KB 388 KB +0.0%
gpt2 num_nodes 53 53 +0.0%
llama model_size_bytes 425 KB 425 KB +0.0%
llama num_nodes 61 61 +0.0%
llama (static-cache) model_size_bytes 425 KB 425 KB +0.0%
llama (static-cache) num_nodes 58 58 +0.0%
mamba (ssm-text-generation) model_size_bytes 360 KB 360 KB +0.0%
mamba (ssm-text-generation) num_nodes 98 98 +0.0%
phi3 model_size_bytes 421 KB 421 KB +0.0%
phi3 num_nodes 59 59 +0.0%
phi3 (static-cache) model_size_bytes 421 KB 421 KB +0.0%
phi3 (static-cache) num_nodes 56 56 +0.0%
qwen2 model_size_bytes 425 KB 425 KB +0.0%
qwen2 num_nodes 61 61 +0.0%
qwen2 (static-cache) model_size_bytes 425 KB 425 KB +0.0%
qwen2 (static-cache) num_nodes 58 58 +0.0%
qwen3_5_moe (hybrid-text-generation) model_size_bytes 506 KB 506 KB +0.0%
qwen3_5_moe (hybrid-text-generation) num_nodes 275 275 +0.0%
qwen3_5_text (hybrid-text-generation) model_size_bytes 458 KB 458 KB +0.0%
qwen3_5_text (hybrid-text-generation) num_nodes 129 129 +0.0%
qwen3_5_vl (hybrid-qwen-vl) model_size_bytes 977 KB 977 KB +0.0%
qwen3_5_vl (hybrid-qwen-vl) num_nodes 408 408 +0.0%
t5 (seq2seq) model_size_bytes 836 KB 836 KB +0.0%
t5 (seq2seq) num_nodes 166 166 +0.0%
whisper (speech-to-text) model_size_bytes 1008 KB 1008 KB +0.0%
whisper (speech-to-text) num_nodes 128 128 +0.0%

No performance regressions.

@justinchuby

Copy link
Copy Markdown
Member Author

Moving changes to PR #183 instead.

@codecov

codecov Bot commented Apr 22, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 6.32911% with 74 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/mobius/_testing/generation.py 6.97% 40 Missing ⚠️
src/mobius/_testing/torch_reference.py 0.00% 34 Missing ⚠️

📢 Thoughts on this report? Let us know!

@justinchuby
justinchuby deleted the justinchu/expand-l4-l5-coverage branch April 22, 2026 00:32

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Expands the golden-test (L4/L5) infrastructure to cover more HuggingFace model/task types, including adding an ONNX-based encoder–decoder (seq2seq) generation loop and introducing ci_skip_reason to distinguish “too large for CI” from “known broken”.

Changes:

  • Add OnnxSeq2SeqGenerator and wire L5 generation to accept task_type=seq2seq (and attempted speech-to-text support).
  • Add ci_skip_reason field support end-to-end (schema, YAML loading, test skipping in CI, dashboard surfacing).
  • Add/refresh substantial golden reference data and per-model skip triage YAMLs across causal LM / seq2seq / encoder / vision / VLM.

Reviewed changes

Copilot reviewed 97 out of 97 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/e2e_golden_test.py CI-only skipping via ci_skip_reason; add seq2seq L5 generation path.
src/mobius/_testing/generation.py Introduce OnnxSeq2SeqGenerator for encoder–decoder greedy decoding.
src/mobius/_testing/golden.py Extend GoldenTestCase + YAML loader to include ci_skip_reason.
src/mobius/_testing/torch_reference.py Improve vision model loading fallbacks and output extraction.
scripts/generate_golden.py Generator robustness: CLIP/X-MOD handling, decoder_start fallback, vision output shape handling, route more vision task types.
scripts/generate_dashboard.py Surface ci_skip_reason in dashboard metadata rows.
testdata/cases/schema.json Add ci_skip_reason to test-case schema.
testdata/golden/vision/swin2sr.json Add L4 golden reference output.
testdata/golden/vision/segformer-b0.json Add L4 golden reference output.
testdata/golden/vision/pvt-v2-b0.json Add L4 golden reference output.
testdata/golden/vision/mobilevitv2-1.0.json Add L4 golden reference output.
testdata/golden/vision/mobilevit-small.json Add L4 golden reference output.
testdata/golden/vision/depth-anything-small.json Add L4 golden reference output.
testdata/golden/vision/cvt-13.json Add L4 golden reference output.
testdata/golden/vision-language/mllama_generation.json Add L5 generation golden reference output.
testdata/golden/vision-language/mllama.json Add L4 golden reference output.
testdata/golden/vision-language/gemma-3-4b-it_generation.json Add L5 generation golden reference output.
testdata/golden/vision-language/gemma-3-4b-it.json Add L4 golden reference output.
testdata/golden/seq2seq/prophetnet-large_generation.json Add L5 generation golden reference output.
testdata/golden/seq2seq/prophetnet-large.json Add L4 golden reference output.
testdata/golden/seq2seq/plbart-base_generation.json Add L5 generation golden reference output.
testdata/golden/seq2seq/plbart-base.json Add L4 golden reference output.
testdata/golden/seq2seq/pegasus-xsum_generation.json Add L5 generation golden reference output.
testdata/golden/seq2seq/pegasus-xsum.json Add L4 golden reference output.
testdata/golden/seq2seq/mbart-large_generation.json Add L5 generation golden reference output.
testdata/golden/seq2seq/mbart-large.json Add L4 golden reference output.
testdata/golden/seq2seq/led-base_generation.json Add L5 generation golden reference output.
testdata/golden/seq2seq/led-base.json Add L4 golden reference output.
testdata/golden/seq2seq/fsmt-wmt19_generation.json Add L5 generation golden reference output.
testdata/golden/seq2seq/fsmt-wmt19.json Add L4 golden reference output.
testdata/golden/seq2seq/bigbird-pegasus-large_generation.json Add L5 generation golden reference output.
testdata/golden/seq2seq/bigbird-pegasus-large.json Add L4 golden reference output.
testdata/golden/encoder/xmod-base.json Add L4 golden reference output.
testdata/golden/encoder/xlm-roberta-xl.json Add L4 golden reference output.
testdata/golden/encoder/roformer-chinese-small.json Add L4 golden reference output.
testdata/golden/encoder/rembert-base.json Add L4 golden reference output.
testdata/golden/encoder/flaubert-base.json Add L4 golden reference output.
testdata/golden/encoder/deberta-v3-base.json Add L4 golden reference output.
testdata/golden/encoder/clip-text-model.json Add L4 golden reference output.
testdata/golden/causal-lm/qwen3-30b-a3b_generation.json Add L5 generation golden reference output.
testdata/golden/causal-lm/qwen3-30b-a3b.json Add L4 golden reference output.
testdata/golden/causal-lm/olmo3-7b_generation.json Add L5 generation golden reference output.
testdata/golden/causal-lm/olmo3-7b.json Add L4 golden reference output.
testdata/golden/causal-lm/granite-moe-shared-7b_generation.json Add L5 generation golden reference output.
testdata/golden/causal-lm/granite-moe-shared-7b.json Add L4 golden reference output.
testdata/golden/causal-lm/glm-4-9b_generation.json Add L5 generation golden reference output.
testdata/golden/causal-lm/glm-4-9b.json Add L4 golden reference output.
testdata/golden/causal-lm/flex-olmo-2x7b_generation.json Add L5 generation golden reference output.
testdata/golden/causal-lm/flex-olmo-2x7b.json Add L4 golden reference output.
testdata/golden/causal-lm/ernie4_5-21b-moe_generation.json Add L5 generation golden reference output.
testdata/golden/causal-lm/ernie4_5-21b-moe.json Add L4 golden reference output.
testdata/golden/causal-lm/arcee-afm-4.5b_generation.json Add L5 generation golden reference output.
testdata/golden/causal-lm/arcee-afm-4.5b.json Add L4 golden reference output.
testdata/golden/causal-lm/apertus-8b_generation.json Add L5 generation golden reference output.
testdata/golden/causal-lm/apertus-8b.json Add L4 golden reference output.
testdata/cases/vision/swin2sr.yaml Add skip_reason triage for known failure.
testdata/cases/vision/segformer-b0.yaml Add skip_reason triage for known failure.
testdata/cases/vision/pvt-v2-b0.yaml Add skip_reason triage for known failure.
testdata/cases/vision/mobilevitv2-1.0.yaml Add skip_reason triage for known failure.
testdata/cases/vision/mobilevit-small.yaml Add skip_reason triage for known failure.
testdata/cases/vision/depth-anything-small.yaml Add skip_reason triage for known failure.
testdata/cases/vision/cvt-13.yaml Add skip_reason triage for known failure.
testdata/cases/vision-language/mllama.yaml Replace gated skip; add ci_skip_reason.
testdata/cases/vision-language/ministral-3-3b.yaml Replace gated skip with concrete failure reason.
testdata/cases/vision-language/gemma-4-e4b-it.yaml Replace gated skip with concrete failure reason.
testdata/cases/vision-language/gemma-3-4b-it.yaml Remove gated skip reason.
testdata/cases/speech/gemma-4-e4b-it-audio.yaml Move “too large” to ci_skip_reason; add concrete skip reason.
testdata/cases/seq2seq/xlm-prophetnet-large.yaml Update skip rationale.
testdata/cases/seq2seq/prophetnet-large.yaml Update level + add skip reason for inference failure.
testdata/cases/seq2seq/plbart-base.yaml Add skip reason (no safetensors).
testdata/cases/seq2seq/pegasus-xsum.yaml Add skip reason (no safetensors) + mark L5.
testdata/cases/seq2seq/mbart-large.yaml Add skip reason (no safetensors) + mark L5.
testdata/cases/seq2seq/led-base.yaml Add skip reason (no safetensors) + mark L5.
testdata/cases/seq2seq/fsmt-wmt19.yaml Add skip reason (no safetensors).
testdata/cases/seq2seq/bigbird-pegasus-large.yaml Mark L5 + add skip reason (no safetensors).
testdata/cases/encoder/xmod-base.yaml Add skip reason (no safetensors).
testdata/cases/encoder/xlm-roberta-xl.yaml Replace CI-size skip with concrete inference failure reason.
testdata/cases/encoder/roformer-chinese-small.yaml Add skip reason (no safetensors).
testdata/cases/encoder/rembert-base.yaml Replace CI-size skip with no-safetensors reason.
testdata/cases/encoder/flaubert-base.yaml Add skip reason for unsupported activation config.
testdata/cases/encoder/deberta-v3-base.yaml Add skip reason (no safetensors).
testdata/cases/encoder/clip-text-model.yaml Add skip reason (model_type not registered).
testdata/cases/causal-lm/zamba2-1_2b.yaml Lower min_token_match_ratio tolerance for L5 divergence.
testdata/cases/causal-lm/qwen3-30b-a3b.yaml Move “too large” to ci_skip_reason.
testdata/cases/causal-lm/olmo3-7b.yaml Move “too large” to ci_skip_reason.
testdata/cases/causal-lm/nemotron-h-nano-4b.yaml Replace CI-size skip with dependency-based skip reason.
testdata/cases/causal-lm/mpt-7b.yaml Replace CI-size skip with model-removed skip reason.
testdata/cases/causal-lm/mixtral-8x7b.yaml Move “too large” to ci_skip_reason.
testdata/cases/causal-lm/granite-moe-shared-7b.yaml Add concrete failure skip + CI-only skip.
testdata/cases/causal-lm/glm-4-9b.yaml Add concrete inference failure skip + CI-only skip.
testdata/cases/causal-lm/gemma-4-e4b.yaml Replace gated skip with concrete failure reason.
testdata/cases/causal-lm/flex-olmo-2x7b.yaml Move “too large” to ci_skip_reason.
testdata/cases/causal-lm/ernie4_5-21b-moe.yaml Add concrete config-missing skip + CI-only skip.
testdata/cases/causal-lm/cohere2-r7b.yaml Clarify gated-access skip reason.
testdata/cases/causal-lm/cohere-r7b.yaml Clarify gated-access skip reason.
testdata/cases/causal-lm/arcee-afm-4.5b.yaml Remove CI-size skip reason.
testdata/cases/causal-lm/apertus-8b.yaml Add concrete activation skip + CI-only skip.

Comment thread tests/e2e_golden_test.py
Comment on lines 1302 to 1310
# Task types that support autoregressive generation.
# seq2seq and speech-to-text require specialised loops not yet implemented.
_GENERATION_SUPPORTED_TASKS = frozenset(
{
"text-generation",
"image-text-to-text",
"seq2seq",
}
)

Copilot AI Apr 22, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_GENERATION_SUPPORTED_TASKS is missing "speech-to-text", so L5 generation cases for speech models will always be skipped by the guard in test_generation_matches_golden(), even though later code tries to handle ("seq2seq", "speech-to-text"). Either add "speech-to-text" to _GENERATION_SUPPORTED_TASKS (if implemented), or remove/update the unreachable branch and the class docstring that claims support.

Copilot uses AI. Check for mistakes.
Comment thread tests/e2e_golden_test.py
Comment on lines +1478 to +1479
elif case.task_type in ("seq2seq", "speech-to-text"):
new_tokens = _run_seq2seq_generation(pkg, case, golden, expected_token_ids)

Copilot AI Apr 22, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new ("seq2seq", "speech-to-text") branch routes speech-to-text models through _run_seq2seq_generation(), but speech-to-text packages (e.g. Whisper) have different I/O contracts (encoder takes input_features, decoder takes decoder_input_ids + position_ids). This path will fail at runtime if "speech-to-text" is ever enabled. Consider adding a dedicated L5 speech-to-text generation loop (similar to _run_speech_to_text_prefill) or extending the generator to handle Whisper-style inputs.

Copilot uses AI. Check for mistakes.
Comment on lines +258 to +264
for _step in range(max_new_tokens):
dec_feeds: dict[str, np.ndarray] = {
"input_ids": cur_dec_ids,
"encoder_hidden_states": enc_hidden,
"attention_mask": np.ones((batch_size, src_seq_len), dtype=np.int64),
**past_kv,
}

Copilot AI Apr 22, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OnnxSeq2SeqGenerator.generate() feeds the decoder attention_mask with shape [batch, src_seq_len] and never grows it across decode steps. In Seq2SeqTask, the decoder input attention_mask is defined as [batch, past_seq_len + dec_seq_len], i.e. it should reflect the decoder time dimension (like OnnxGenerator does for causal LM). This can break models that use the decoder mask (and can also fail shape checks depending on runtime). Build attention_mask from the current decoder length (past_seq_len + cur_dec_ids.shape[1]) and update past_seq_len each step.

Copilot uses AI. Check for mistakes.
Comment on lines +234 to +253
num_kv_heads = self.config.num_key_value_heads
head_dim = self.config.head_dim
past_kv: dict[str, np.ndarray] = {}

for name in self.dec_session.input_names:
if not name.startswith("past_key_values."):
continue
if ".cross." in name:
# Cross-attention cache: starts empty, populated on first step
past_kv[name] = np.zeros(
(batch_size, num_kv_heads, 0, head_dim),
dtype=np.float32,
)
else:
# Self-attention cache: grows each step
past_kv[name] = np.zeros(
(batch_size, num_kv_heads, 0, head_dim),
dtype=np.float32,
)

Copilot AI Apr 22, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

KV cache initialization in OnnxSeq2SeqGenerator hard-codes (batch_size, config.num_key_value_heads, 0, config.head_dim) for every past_key_values.* input. However Seq2SeqTask creates caches using num_attention_heads (and dtype from the model), and naming is split into .self. and .cross.. To avoid head-count mismatches and future shape variations, initialize caches from dec_session.get_input_shape(name) (like _make_empty_kv_cache() does in tests/e2e_golden_test.py) and only override the sequence dimension (0 for self; encoder seq len for cross if needed).

Suggested change
num_kv_heads = self.config.num_key_value_heads
head_dim = self.config.head_dim
past_kv: dict[str, np.ndarray] = {}
for name in self.dec_session.input_names:
if not name.startswith("past_key_values."):
continue
if ".cross." in name:
# Cross-attention cache: starts empty, populated on first step
past_kv[name] = np.zeros(
(batch_size, num_kv_heads, 0, head_dim),
dtype=np.float32,
)
else:
# Self-attention cache: grows each step
past_kv[name] = np.zeros(
(batch_size, num_kv_heads, 0, head_dim),
dtype=np.float32,
)
past_kv: dict[str, np.ndarray] = {}
for name in self.dec_session.input_names:
if not name.startswith("past_key_values."):
continue
# Build the cache tensor from the decoder input contract so the
# helper follows the model's declared head count and layout.
cache_shape = list(self.dec_session.get_input_shape(name))
if len(cache_shape) >= 1:
cache_shape[0] = batch_size
if len(cache_shape) >= 3:
if ".cross." in name:
# Cross-attention cache is keyed by encoder time steps.
cache_shape[2] = src_seq_len
else:
# Self-attention cache starts empty and grows each step.
cache_shape[2] = 0
past_kv[name] = np.zeros(tuple(cache_shape), dtype=np.float32)

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants