Skip to content

fix(native): improve backend diagnostics and context handling - #1158

Closed
justinchuby wants to merge 1 commit into
mainfrom
split/native-backend-diagnostics
Closed

justinchuby wants to merge 1 commit into
mainfrom
split/native-backend-diagnostics

Conversation

@justinchuby

Copy link
Copy Markdown
Owner

Summary

  • make native backend failures identify unsupported components and operators
  • preserve model context limits in pipeline loading
  • improve native/ORT support-library diagnostics

Validation

  • pipeline executor test suite

Propagate pipeline context metadata, distinguish the ORT support library from backend selection, and report native CUDA whole-session fallback explicitly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 0190e2eb-abe4-451f-b36d-44a035a99b7e
@justinchuby

Copy link
Copy Markdown
Owner Author

Rebased again onto latest main after its sibling PRs landed and re-resolved the four conflict hunks in pipeline/mod.rs. All four are independent additions -- generation_defaults (from #1162) and max_sequence_length (this PR) touching the same struct field list, accessor block, �uild locals, and constructor -- so the resolution is to keep both on every hunk. The accessor hunk needed a closing brace inserted, since the two sides had been sharing one.

Re-validated after the rebase: cargo test -p onnx-genai-engine --no-default-features --features native-backend --test pipeline_executor -> 2 passed, including pipeline_exposes_declared_context_limit. The reviewer had already falsified that test (dropping the .or(self.max_sequence_length) reddens it with left: None, right: Some(16)).

Merging.

@justinchuby
justinchuby force-pushed the split/native-backend-diagnostics branch from 4a7c011 to b2f295b Compare August 18, 2026 02:45
justinchuby added a commit that referenced this pull request Aug 18, 2026
#1158) (#1200)

Same content as #1158, reopened on a branch based directly on `main`.

#1158 was reviewed, rebased and validated, but could not be merged: it
is the base of #1161, and GitHub refuses both `gh pr merge` and a base
change for stacked pull requests, directing callers to an asynchronous
merge endpoint this repository does not expose (404). Rather than leave
two reviewed PRs stuck behind an API we cannot reach, the commit is
cherry-picked onto `main` here and #1158 is closed. #1161 is then free
to rebase onto `main` normally.

The rebase produced four conflicts in `pipeline/mod.rs`, all the same
shape as the earlier ones in this family: this branch adds
`max_sequence_length` and `effective_max_context` while `main` has since
added `generation_defaults`. They are independent additions in the same
struct, constructor and accessor block, so both sides are kept; the
accessor hunk needed a closing brace reinserted because the two sides
shared one.

Validation on the rebased tree: `pipeline_executor` 2 passed, and the
engine lib suite 508 passed / 0 failed / 1 ignored, matching the
baseline on `main`.

Copilot-Session: 0190e2eb-abe4-451f-b36d-44a035a99b7e
@justinchuby

Copy link
Copy Markdown
Owner Author

Closing in favour of #1200, which carries this exact commit on a branch based directly on main.

This PR could not be merged: it is the base of #1161, and GitHub blocks both merge and base changes for stacked pull requests, pointing at an asynchronous merge endpoint this repository returns 404 for. The content was reviewed and validated, so it is not being dropped -- only re-landed off the stack, which also unblocks #1161.

@codecov

codecov Bot commented Aug 18, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 79.85%. Comparing base (4435c51) to head (b2f295b).
⚠️ Report is 20 commits behind head on main.

Files with missing lines Patch % Lines
...es/onnx-runtime-ep-cpu/src/kernels/matmul_nbits.rs 0.00% 4 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #1158      +/-   ##
==========================================
+ Coverage   79.60%   79.85%   +0.25%     
==========================================
  Files         357      359       +2     
  Lines      153790   157326    +3536     
  Branches   153790   157326    +3536     
==========================================
+ Hits       122419   125631    +3212     
- Misses      26850    27135     +285     
- Partials     4521     4560      +39     
Flag Coverage Δ
mlas 85.39% <ø> (?)
offline 79.74% <0.00%> (+0.14%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...es/onnx-runtime-ep-cpu/src/kernels/matmul_nbits.rs 75.88% <0.00%> (-0.14%) ⬇️

... and 18 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions

Copy link
Copy Markdown

🔴 Benchmark Regression Detected

Comparison of criterion micro-benchmarks: PR head vs merge-base, measured on the same runner in the same job (base first → PR second).

ℹ️ Absolute times are informational only — they vary with runner load. The % change column is the reliable signal because both sides ran under identical conditions.

Status Scenario Base PR Change
🔴 matmul/small_generic_f32_threads=8/1x256x256 45.10 µs 90.50 µs +100.7%
🔴 matmul/small_generic_bf16_threads=8/1x256x256 37.07 µs 51.27 µs +38.3%
🔴 matmul/small_generic_f16_threads=8/1x256x256 36.15 µs 48.76 µs +34.9%
⚠️ grammar_masking/llguidance_compute_mask/32 70.30 µs 88.00 µs +25.2%
✅ reduce_mean/medium_f32_threads=1-internal/65536 235.67 µs 269.02 µs +14.2%
✅ kv_cache/alloc_dealloc_pages 37.39 µs 39.78 µs +6.4%
✅ logit_processing/seven_processor_chain_per_step 299.82 µs 318.20 µs +6.1%
✅ block_quantized_matmul_cached_dense/mxfp4_cached_dense_repeated_call/1x1024x1024 42.43 µs 44.94 µs +5.9%
✅ add/large_bf16_threads=1-internal/4194304 1.54 ms 1.62 ms +5.2%
✅ qwen3_sampling_processors/top_k_top_p_fast 612.11 µs 643.76 µs +5.2%
✅ matmul/medium_generic_f32_threads=1/32x512x512 2.25 ms 2.36 ms +5.0%
✅ gather/small_f32_threads=1-internal/4096 622.8 ns 653.0 ns +4.9%
✅ matmul/large_generic_bf16_threads=8/32x1024x1024 1.25 ms 1.31 ms +4.8%
✅ add/small_bf16_threads=1-internal/1024 411.3 ns 424.9 ns +3.3%
✅ add/small_f16_threads=1-internal/1024 424.3 ns 438.0 ns +3.2%
✅ add/medium_f32_threads=1-internal/262144 22.62 µs 23.18 µs +2.5%
✅ reduce_mean/large_f32_threads=1-internal/262144 922.63 µs 942.90 µs +2.2%
✅ matmul/large_generic_f32_threads=8/32x1024x1024 3.58 ms 3.65 ms +1.9%
✅ gather/medium_bf16_threads=1-internal/32768 2.42 µs 2.46 µs +1.8%
✅ block_quantized_matmul_cached_dense/mxfp4_uncached_dequant_each_call/1x1024x1024 507.04 µs 514.86 µs +1.5%
✅ matmul/medium_generic_f16_threads=1/32x512x512 32.67 µs 32.85 µs +0.5%
✅ qwen3_sampling_processors/top_p_fast_after_top_k 534.24 µs 536.28 µs +0.4%
✅ reduce_mean/small_f32_threads=1-internal/4096 13.99 µs 14.02 µs +0.2%
✅ block_quantized_moe_cached_dense/mxfp4_uncached_expert_dequant_each_call/rows=1,H=256,I=256,E=4,top_k=1 431.97 µs 431.49 µs -0.1%
✅ add/large_f16_threads=1-internal/4194304 1.54 ms 1.53 ms -0.4%
✅ matmul/large_generic_bf16_threads=1/32x1024x1024 1.81 ms 1.80 ms -0.6%
✅ matmul/large_generic_f16_threads=8/32x1024x1024 78.39 µs 77.51 µs -1.1%
✅ add/medium_bf16_threads=1-internal/262144 96.22 µs 94.85 µs -1.4%
✅ sampling_latency/min_p_per_token 198.94 µs 195.68 µs -1.6%
✅ matmul/large_generic_f16_threads=1/32x1024x1024 74.48 µs 72.77 µs -2.3%
✅ gather/small_f16_threads=1-internal/4096 463.2 ns 448.3 ns -3.2%
✅ sampling_latency/top_p_per_token 370.49 µs 354.86 µs -4.2%
✅ gather/large_f32_threads=1-internal/131072 30.93 µs 29.53 µs -4.5%
✅ add/small_f32_threads=1-internal/1024 187.5 ns 178.7 ns -4.7%
✅ qwen3_sampling_processors/top_k_top_p_full_sort_baseline 5.88 ms 5.55 ms -5.6%
✅ matmul/large_generic_f32_threads=1/32x1024x1024 9.15 ms 8.55 ms -6.6%
✅ qwen3_sampling_processors/top_k_full_sort_baseline 2.17 ms 1.98 ms -8.7%
✅ matmul/medium_generic_bf16_threads=1/32x512x512 535.70 µs 489.00 µs -8.7%
✅ qwen3_sampling_processors/top_p_full_sort_after_top_k_baseline 3.59 ms 3.26 ms -9.1%
✅ gather/small_bf16_threads=1-internal/4096 501.1 ns 454.7 ns -9.3%
✅ sampling_latency/greedy_per_token 3.40 µs 3.08 µs -9.5%
✅ tokenization/decode_tokens_per_second 6.48 ms 5.84 ms -9.8%
✅ add/large_f32_threads=1-internal/4194304 623.88 µs 560.06 µs -10.2%
✅ matmul/small_generic_f16_threads=1/1x256x256 33.24 µs 29.66 µs -10.8%
✅ block_quantized_moe_cached_dense/mxfp4_cached_dense_expert_repeated_call/rows=1,H=256,I=256,E=4,top_k=1 176.60 µs 156.52 µs -11.4%
✅ gather/medium_f16_threads=1-internal/32768 2.58 µs 2.28 µs -11.4%
✅ sampling_latency/top_k_per_token 55.47 µs 48.43 µs -12.7%
✅ matmul/small_generic_bf16_threads=1/1x256x256 34.96 µs 30.48 µs -12.8%
✅ qwen3_sampling_processors/top_k_partial_selection 158.11 µs 135.82 µs -14.1%
✅ block_quantized_matmul_cached_dense/mxfp4_preexpanded_dense_oncelock_like_proxy/1x1024x1024 47.94 µs 40.90 µs -14.7%
✅ add/medium_f16_threads=1-internal/262144 116.36 µs 99.04 µs -14.9%
🟢 matmul/medium_generic_f16_threads=8/32x512x512 38.34 µs 31.82 µs -17.0%
🟢 gather/large_f16_threads=1-internal/131072 16.69 µs 13.78 µs -17.4%
🟢 tokenization/encode_tokens_per_second 421.65 µs 347.89 µs -17.5%
🟢 gather/medium_f32_threads=1-internal/32768 4.40 µs 3.61 µs -18.1%
🟢 matmul/medium_generic_f32_threads=8/32x512x512 1.26 ms 996.06 µs -20.8%
🟢 matmul/medium_generic_bf16_threads=8/32x512x512 460.44 µs 353.99 µs -23.1%
🟢 gather/large_bf16_threads=1-internal/131072 18.55 µs 14.22 µs -23.3%
🟢 matmul/small_generic_f32_threads=1/1x256x256 51.97 µs 35.22 µs -32.2%

Visual flags: ⚠️ ≥ 15% slower, 🔴 ≥ 30% slower — calibrated against measured runner noise (~27% worst-case on multi-threaded matmul)

Host info
CPU: Apple M1 (Virtual)
Cores: 3
OS: Darwin 25.5.0 arm64
Rust: rustc 1.97.1 (8bab26f4f 2026-07-14)
Load avg: { 4.83 3.97 4.81 }
What this cannot catch
  • Regressions in code paths not covered by these benchmarks (e.g., end-to-end decode with a real model)
  • Sub-threshold regressions that compound over multiple PRs
  • Performance changes that only manifest under GPU execution
  • Latency changes in the ORT integration path (these benchmarks exercise the native Rust kernels)

justinchuby added a commit that referenced this pull request Aug 18, 2026
…as silence (#1214)

Unstacked replacement for #1161, which GitHub will not let us merge: it
was stacked on #1158, and the stack metadata survives even though that
PR is now closed and its content is in main via #1200. `gh pr merge`,
`gh pr edit --base`, and both async merge endpoints all refuse or 404.
Same branch content, off `main`, no stack.

## What it does

Formats structured model responses in the CLI, and fixes a
silent-empty-reply bug found while reviewing it.

`load_response_config` succeeds from `tokenizer_config.json` alone, but
the marker strings are only resolved to token ids later, against the
tokenizer. If a template names a marker the tokenizer does not encode as
a single special token, `message_token_id` stays `None` -- and
`ResponseStream` starts in `Header`, whose only exit is
`message_token_id == Some(token.token_id)`. `None` never equals
`Some(_)`, so the parser is trapped in a state where every output
carries `include_in_reply: false`. **The caller gets an empty string and
no error: a good generation reported as silence.**

The fix withdraws structured mode entirely rather than degrading in
place. Raw output shows the markers as literal text -- a poorer
presentation, but an honest one. Silence is neither.

It is a mechanism, not a patch: `bind_response_tokens` is the single
guarded entry point, and `bind_token_ids` is now module-private so no
future caller can bind without the check. Only `message_token_id` is
treated as load-bearing; withdrawing on the optional markers would drop
structured mode for templates that parse fine.

## Validation

- `onnx-genai-cli` lib: **121 passed / 0 failed**
- engine lib: **508 / 0 / 1**, matching main's baseline
- Both new tests falsified: forcing `is_parseable` to `true` turns them
RED on the exact assertion they claim to guard
- The two `repl_e2e` attachment failures (`missing required pipeline
input 'encoder.pixel_values'`) reproduce identically on unmodified main
and are unrelated to this branch

Supersedes #1161.

---------

Co-authored-by: justinchuby <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0190e2eb-abe4-451f-b36d-44a035a99b7e
Copilot-Session: d60eb808-7cc6-4abc-b48d-2a6dd3841624
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.

1 participant